libarchive - Extract to specified directory - c++

I have a tar file I want to extract with libarchive to a specific directory. How can I make libarchive extract into to any directory I want? At the moment it always extracts into my program's working directory. I looked at this answer but all this does is change the location of an archive entry within the archive, i.e. it still extracts into my program's working directory just at a different sub-directory.

I resolved this issue next way:
(insert this code before calling 'archive_write_header' function)
const char* currentFile = archive_entry_pathname(archiveEntry);
const std::string fullOutputPath = destination + currentFile;
archive_entry_set_pathname(archiveEntry, fullOutputPath.c_str());
where destination is output path.
And it works.

From the libarchive discussion boards:
"It depends, of course, on the archive being extracted.
Typically, you would chdir() to the directory where you want the output to go, then use code similar to that in the Wiki Examples page:
A Complete Extractor Example
or in the untar.c sample program:
untar Example
Of course, if the tar file you're extracting has
interesting filenames (such as "c:\someotherdirectory"),
then you'll need to play with the filenames as you extract.
Note that the examples all use archive_read_next_header()
to get an entry object from the input archive describing
the next entry; you are then free to edit that entry description
in any way you wish -- in particular, you can change the
name, owner, or permissions -- before calling
archive_write_header() to recreate the entry on
disk.
The Examples page in the Wiki above is probably the
best place to start."

A longer C version of Alex's answer. To extract files to a temp_dir, use archive_entry_pathname() and archive_entry_set_pathname() to re-write each entry::
char* dest_file;
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
break;
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(a));
if (r < ARCHIVE_WARN)
return NULL;
asprintf(&dest_file, "%s/%s", temp_dir, archive_entry_pathname(entry));
archive_entry_set_pathname(entry, dest_file);
// printf(" writing %s\n", dest_file);
r = archive_write_header(ext, entry);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
else if (archive_entry_size(entry) > 0) {
r = copy_data(a, ext);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
if (r < ARCHIVE_WARN)
exit(1);
}
r = archive_write_finish_entry(ext);
if (r < ARCHIVE_OK)
fprintf(stderr, "%s\n", archive_error_string(ext));
if (r < ARCHIVE_WARN)
return NULL;
free(dest_file);
}

Related

Decompressing a zip containing files with Cyrillic names with lbarchive

For my C++ project I need a library to decompress various formats of archives. I found libarchive was the best solution for this, however when I started testing it I ran into a problem. I tried running an untar example from the library over a zip containing files named in Russian and it just refuses to work.
archive_read_support_compression_all(a);
archive_read_support_format_all(a);
archive_read_open_filename(a, filename, 10240)));
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF) {
break;
}
if (r != ARCHIVE_OK) {
if (r == ARCHIVE_WARN) {
warn("archive_read_next_header()", archive_error_string(a)); // This warning goes off
} else {
fail("archive_read_next_header()", archive_error_string(a), 1);
}
}
msg(archive_entry_pathname(entry)); // entry is null
}
}
When I run this over other archive formats containing the same files everything is fime but for zip it fails with
archive_read_next_header() failed: Pathname cannot be converted from UTF-8 to current locale.
What can I do to make this work? I'm generally fine with the names being gibberish at this point
The system I'm running is Ubuntu 20.04 if that's important

Libzip - Error: Error while opening the archive : no error

I'm trying to find out the solution to solve a problem;
In fact, i'm writing my own tool to make saves using libzip in C++ to compress the files.
Absolutly not finished but i wanted to make some tests, then i do and obtain a "funny" error from the log.
Here's my function:
void save(std::vector<std::string> filepath, std::string savepath){
int err;
savepath += time(NULL);
zip* saveArchive = zip_open(savepath.c_str(), ZIP_CREATE , &err);
if(err != ZIP_ER_OK) throw xif::sys_error("Error while opening the archive", zip_strerror(saveArchive));
for(int i = 0; i < filepath.size(); i++){
if(filepath[i].find("/") == std::string::npos){}
if(filepath[i].find(".cfg") == std::string::npos){
err = (int) zip_file_add(saveArchive, filepath[i].c_str(), NULL, NULL);
if(err == -1) throw xif::sys_error("Error while adding the files", zip_strerror(saveArchive));
}
}
if(zip_close(saveArchive) == -1) throw xif::sys_error("Error while closing the archive", zip_strerror(saveArchive));
}
I get a => Error : Error while opening the archive : No error
And, of course, i didn't have any .zip written.
If you could help me, thanks to you !
The documentation for zip_open says that it only sets *errorp if the open fails. Either test for saveArchive == nullptr or initialize err to
ZIP_ER_OK.
P.S. The search for '/' does nothing. Did you mean to put a continue in that block?
The other problematic line is:
savepath += time(NULL);
If that is the standard time function, that returns a time in seconds since the epoch. That will probably get truncated to a char, and then that char appended to the file name. That will cause strange characters to appear in the filename! I suggest using std::chrono to convert to text.

libzip: validating a zip file before loading

I use libzip to open zip files in my application and in order to ensure good behavior in case of corrupt zip files I manually corrupted a zip file (by removing a few random lines with a text editor) and try to load that file. However this hangs the entire app because zip_fread() never returns.
Is there a where to determine if a zip file is valid before loading it to avoid such situations?
Update
The behavior seems to depend on the version, so I probably only need to update. This is the code I use on Windows, Mac OS and Linux:
int err;
zip *z= zip_open(zipfile.c_str(), 0, &err);
if (!z)
{
if (err == ZIP_ER_NOZIP)
throw std::runtime_error("The file is not a Workbench document.");
else if (err == ZIP_ER_MEMORY)
throw grt::os_error("Cannot allocate enough memory to open document.");
else if (err == ZIP_ER_NOENT)
throw grt::os_error("File not found.");
int len= zip_error_to_str(NULL, 0, 0, err);
std::string msg;
if (len > 0)
{
char *buf= (char*)g_malloc(len+1);
zip_error_to_str(buf, len+1, 0, err);
msg= buf;
g_free(buf);
}
else
msg= "error opening zip archive";
zip_close(z);
throw std::runtime_error(strfmt(_("Cannot open document file: %s"), msg.c_str()));
}
On OS X this fragment does not return an error (I used the same file for all platforms). Instead the following zip_read() call just hangs. On the other platforms zip_read() immediately returns with a result < 0, so it's easy to catch the error there.

Select the last modified file from a directory

I need to know, how I can select the Last modified/created file in a given directory.
I currently have a directory named XML, and inside that there are many XML files. But I would like to select only the last modified file.
I use the following function to list all the items inside a folder. It writes all the files in a string vector, but you can change that.
bool ListContents (vector<string>& dest, string dir, string filter, bool recursively)
{
WIN32_FIND_DATAA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
DWORD dwError = 0;
// Prepare string
if (dir.back() != '\\') dir += "\\";
// Safety check
if (dir.length() >= MAX_PATH) {
Error("Cannot open folder %s: path too long", dir.c_str());
return false;
}
// First entry in directory
hFind = FindFirstFileA((dir + filter).c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
Error("Cannot open folder in folder %s: error accessing first entry.", dir.c_str());
return false;
}
// List files in directory
do {
// Ignore . and .. folders, they cause stack overflow
if (strcmp(ffd.cFileName, ".") == 0) continue;
if (strcmp(ffd.cFileName, "..") == 0) continue;
// Is directory?
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// Go inside recursively
if (recursively)
ListContents(dest, dir + ffd.cFileName, filter, recursively, content_type);
}
// Add file to our list
else dest.push_back(dir + ffd.cFileName);
} while (FindNextFileA(hFind, &ffd));
// Get last error
dwError = GetLastError();
if (dwError != ERROR_NO_MORE_FILES) {
Error("Error reading file list in folder %s.", dir.c_str());
return false;
}
return true;
}
(don't forget to include windows.h)
What you have to do is adapt it to find the newest file.
The ffd structure (WIN32_FIND_DATAA data type) contains ftCreationTime, ftLastAccessTime and ftLastWriteTime, you can use those to find the newest file.
These members are FILETIME structures, you can find the documentation here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284%28v=vs.85%29.aspx
You can use FindFirstFile and FindNextFile, they deliver a struct describing the file like size as well as modified time.
Boost.Filesystem offers last_write_time. You can use this to sort the files in a directory. Boost.Filesystem and (Boost) in general can be a little bit intimidating for a C++ new-comer so you might want to check a solution for your OS first.

C zip library that can create password protected zip files on windows?

Anyone know of a C library that can create password protected zip files on windows? It appears that the option to password protect zip files with the built-in zip utility has been removed from windows 7, but I don't think this is an issue.
Can either zziplib or the 7-Zip SDK do this?
7-Zip SDK (LZMA SDK) supports password protected archive.
Related SO post:
https://stackoverflow.com/questions/221049/how-secure-is-7-zip
LZMA SDK:
http://www.7-zip.org/sdk.html
If you can use .NET, check out DotNetZip: http://dotnetzip.codeplex.com/
C++ .NET example to create password-protected ZIP:
http://cheeso.members.winisp.net/DotNetZipHelp/Code%20Examples/Cpp.htm
MINIZIP + zlib supports AES 256 encryption, and is very easy to use!
Code based on minizip unzip.c.
include stdio.h zip.h unzip.h
First, create a zip with a file inside with a password.
The zip file must be in the same directory as the executable.
Run the program from a prompt in the directory of the generated program. This example only extracts the first file!
/*-----------start-------------- */
/*Tries to open the zip in the current directory.*/
unzFile zfile = unzOpen("teste.zip");
if(zfile==NULL)
{
printf("Error!");
return;
}
printf("OK Zip teste.zip opened...\n");
int err = unzGoToFirstFile(zfile);/*go to first file in zip*/
if (err != UNZ_OK)
{
printf("error %d with zipfile in unzGoToFirstFile\n", err);
unzClose(zfile);/*close zip*/
}
/*At this point zfile points to the first file contained in the zip*/
char filename_inzip[256] = {0};/* The file name will be returned here */
unz_file_info file_info = {0};/*strcuture with info of the first file*/
err = unzGetCurrentFileInfo(zfile, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
if (err != UNZ_OK)
{
printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
}
else
{
int len = 8192;/*size of chunk*/
char buffer[8192]={0};/*buffer used to save uncompressed data*/
printf("name of first file is :%s\n",filename_inzip);
printf("uncompressed_size = %d\n",file_info.uncompressed_size);
/*Use your password here, the same one used to create your zip */
err = unzOpenCurrentFilePassword(zfile, "yourpassword");
if (err != UNZ_OK)
printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err);
else
printf("password ok\n");
FILE *fp = fopen(filename_inzip, "wb");/*file for data binary type*/
if (fp != NULL)
{
do
{/*read the current file returned by unzGoToFirstFile to buffer in chunks of the 8192*/
err = unzReadCurrentFile(zfile, &buffer, len );
if (err < 0)
{
printf("error %d with zipfile in unzReadCurrentFile\n", err);
break;
}
if (err == 0)
break;
/*Save the chunk read to the file*/
if (fwrite(&buffer, err, 1, fp) != 1)/*if error break*/
{
printf("error %d in writing extracted file\n", errno);
err = UNZ_ERRNO;
break;
}/*else continue*/
}
while (err > 0);
/*close file*/
fclose(fp);
}
}
unzClose(zfile);