/******************************* LICENCE **************************************
* Any code in this file may be redistributed or modified under the terms of
* the GNU General Public Licence as published by the Free Software
* Foundation; version 2 of the licence.
****************************** END LICENCE ***********************************/
/******************************************************************************
* Author:
* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php
*
* Contributors:
*
******************************************************************************/
#include <stdbool.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
#include "bk.h"
#include "bkInternal.h"
#include "bkExtract.h"
#include "bkPath.h"
#include "bkError.h"
#include "bkMisc.h"
/*******************************************************************************
* bk_extract_boot_record()
* Extracts the el torito boot record to the file destPathAndName, with
* permissions destFilePerms.
* */
int bk_extract_boot_record(VolInfo* volInfo, const char* destPathAndName,
unsigned destFilePerms)
{
int srcFile; /* returned by open() */
bool srcFileWasOpened;
int destFile; /* returned by open() */
int rc;
if(volInfo->bootMediaType == BOOT_MEDIA_NONE)
return BKERROR_EXTRACT_ABSENT_BOOT_RECORD;
if(volInfo->bootMediaType != BOOT_MEDIA_NO_EMULATION &&
volInfo->bootMediaType != BOOT_MEDIA_1_2_FLOPPY &&
volInfo->bootMediaType != BOOT_MEDIA_1_44_FLOPPY &&
volInfo->bootMediaType != BOOT_MEDIA_2_88_FLOPPY)
{
return BKERROR_EXTRACT_UNKNOWN_BOOT_MEDIA;
}
/* SET source file (open if needed) */
if(volInfo->bootRecordIsVisible)
/* boot record is a file in the tree */
{
if(volInfo->bootRecordOnImage->onImage)
{
srcFile = volInfo->imageForReading;
lseek(volInfo->imageForReading, volInfo->bootRecordOnImage->position, SEEK_SET);
srcFileWasOpened = false;
}
else
{
srcFile = open(volInfo->bootRecordOnImage->pathAndName, O_RDONLY);
if(srcFile == -1)
return BKERROR_OPEN_READ_FAILED;
srcFileWasOpened = true;
}
}
else
/* boot record is not a file in the tree */
{
if(volInfo->bootRecordIsOnImage)
{
srcFile = volInfo->imageForReading;
lseek(volInfo->imageForReading, volInfo->bootRecordOffset, SEEK_SET);
srcFileWasOpened = false;
}
else
{
srcFile = open(volInfo->bootRecordPathAndName, O_RDONLY);
if(srcFile == -1)
return BKERROR_OPEN_READ_FAILED;
srcFileWasOpened = true;
}
}
/* END SET source file (open if needed) */
destFile = open(destPathAndName, O_WRONLY | O_CREAT | O_TRUNC,
destFilePerms);
if(destFile == -1)
{
if(srcFileWasOpened)
close(srcFile);
return BKERROR_OPEN_WRITE_FAILED;
}
rc = copyByteBlock(volInfo, srcFile, destFile, volInfo->bootRecordSize);
if(rc <= 0)
{
if(srcFileWasOpened)
close(srcFile);
return rc;
}
close(destFile);
if(srcFileWasOpened)
close(srcFile);
return 1;
}
int bk_extract(VolInfo* volInfo, const char* srcPathAndName,
const char* destDir, bool keepPermissions,
void(*progressFunction)(VolInfo*))
{
return bk_extract_as(volInfo, srcPathAndName, destDir, NULL,
keepPermissions, progressFunction);
}
int bk_extract_as(VolInfo* volInfo, const char* srcPathAndName,
const char* destDir, const char* nameToUse,
bool keepPermissions, void(*progressFunction)(VolInfo*))
{
int rc;
NewPath srcPath;
BkDir* parentDir;
bool dirFound;
volInfo->progressFunction = progressFunction;
volInfo->stopOperation = false;
rc = makeNewPathFromString(srcPathAndName, &srcPath);
if(rc <= 0)
{
freePathContents(&srcPath);
return rc;
}
if(srcPath.numChildren == 0)
{
freePathContents(&srcPath);
return BKERROR_EXTRACT_ROOT;
}
/* i want the parent directory */
srcPath.numChildren--;
dirFound = findDirByNewPath(&srcPath, &(volInfo->dirTree), &parentDir);
srcPath.numChildren++;
if(!dirFound)
{
freePathContents(&srcPath);
return BKERROR_DIR_NOT_FOUND_ON_IMAGE;
}
rc = extract(volInfo, parentDir, srcPath.children[srcPath.numChildren - 1],
destDir, nameToUse, keepPermissions);
freePathContents(&srcPath);
if(rc <= 0)
{
return rc;
}
return 1;
}
int copyByteBlock(VolInfo* volInfo, int src, int dest, unsigned numBytes)
{
int rc;
int count;
int numBlocks;
int sizeLastBlock;
numBlocks = numBytes / READ_WRITE_BUFFER_SIZE;
sizeLastBlock = numBytes % READ_WRITE_BUFFER_SIZE;
maybeUpdateProgress(volInfo);
if(volInfo->stopOperation)
return BKERROR_OPER_CANCELED_BY_USER;
for(count = 0; count < numBlocks; count++)
{
maybeUpdateProgress(volInfo);
if(volInfo->stopOperation)
return BKERROR_OPER_CANCELED_BY_USER;
rc = read(src, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE);
if(rc != READ_WRITE_BUFFER_SIZE)
return BKERROR_READ_GENERIC;
rc = write(dest, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE);
if(rc <= 0)
return rc;
}
if(sizeLastBlock > 0)
{
rc = read(src, volInfo->readWriteBuffer, sizeLastBlock);
if(rc != sizeLastBlock)
return BKERROR_READ_GENERIC;
rc = write(dest, volInfo->readWriteBuffer, sizeLastBlock);
if(rc <= 0)
return rc;
}
return 1;
}
int extract(VolInfo* volInfo, BkDir* parentDir, char* nameToExtract,
const char* destDir, const char* nameToUse, bool keepPermissions)
{
BkFileBase* child;
int rc;
child = parentDir->children;
while(child != NULL)
{
if(volInfo->stopOperation)
return BKERROR_OPER_CANCELED_BY_USER;
if(strcmp(child->name, nameToExtract) == 0)
{
if( IS_DIR(child->posixFileMode) )
{
rc = extractDir(volInfo, BK_DIR_PTR(child), destDir,
nameToUse, keepPermissions);
}
else if ( IS_REG_FILE(child->posixFileMode) )
{
rc = extractFile(volInfo, BK_FILE_PTR(child), destDir,
nameToUse, keepPermissions);
}
else if ( IS_SYMLINK(child->posixFileMode) )
{
rc = extractSymlink(BK_SYMLINK_PTR(child), destDir,
nameToUse);
}
else
{
printf("trying to extract something that's not a file, "
"symlink or directory, ignored\n");fflush(NULL);
}
if(rc <= 0)
{
bool goOn;
if(volInfo->warningCbk != NULL && !volInfo->stopOperation)
/* perhaps the user wants to ignore this failure */
{
snprintf(volInfo->warningMessage, BK_WARNING_MAX_LEN,
"Failed to extract item '%s': '%s'",
child->name,
bk_get_error_string(rc));
goOn = volInfo->warningCbk(volInfo->warningMessage);
rc = BKWARNING_OPER_PARTLY_FAILED;
}
else
goOn = false;
if(!goOn)
{
volInfo->stopOperation = true;
return rc;
}
}
}
child = child->next;
}
return 1;
}
int extractDir(VolInfo* volInfo, BkDir* srcDir, const char* destDir,
const char* nameToUse, bool keepPermissions)
{
int rc;
BkFileBase* child;
/* vars to create destination dir */
char* newDestDir;
unsigned destDirPerms;
/* CREATE destination dir on filesystem */
/* 1 for '\0' */
if(nameToUse == NULL)
newDestDir = malloc(strlen(destDir) + strlen(BK_BASE_PTR(srcDir)->name) + 2);
else
newDestDir = malloc(strlen(destDir) + strlen(nameToUse) + 2);
if(newDestDir == NULL)
return BKERROR_OUT_OF_MEMORY;
strcpy(newDestDir, destDir);
if(destDir[strlen(destDir) - 1] != '/')
strcat(newDestDir, "/");
if(nameToUse == NULL)
strcat(newDestDir, BK_BASE_PTR(srcDir)->name);
else
strcat(newDestDir, nameToUse);
if(keepPermissions)
destDirPerms = BK_BASE_PTR(BK_BASE_PTR(srcDir))->posixFileMode;
else
destDirPerms = volInfo->posixDirDefaults;
/* want to make sure user has write and execute permissions to new directory
* so that can extract stuff into it */
destDirPerms |= 0300;
if(access(newDestDir, F_OK) == 0)
{
free(newDestDir);
return BKERROR_DUPLICATE_EXTRACT;
}
rc = mkdir(newDestDir, destDirPerms);
if(rc == -1)
{
free(newDestDir);
return BKERROR_MKDIR_FAILED;
}
/* END CREATE destination dir on filesystem */
/* EXTRACT children */
child = srcDir->children;
while(child != NULL)
{
rc = extract(volInfo, srcDir, child->name, newDestDir,
NULL, keepPermissions);
if(rc <= 0)
{
free(newDestDir);
return rc;
}
child = child->next;
}
/* END EXTRACT children */
free(newDestDir);
return 1;
}
int extractFile(VolInfo* volInfo, BkFile* srcFileInTree, const char* destDir,
const char* nameToUse, bool keepPermissions)
{
int srcFile;
bool srcFileWasOpened;
char* destPathAndName;
unsigned destFilePerms;
int destFile; /* returned by open() */
int rc;
if(srcFileInTree->onImage)
{
srcFile = volInfo->imageForReading;
lseek(volInfo->imageForReading, srcFileInTree->position, SEEK_SET);
srcFileWasOpened = false;
}
else
{
srcFile = open(srcFileInTree->pathAndName, O_RDONLY);
if(srcFile == -1)
return BKERROR_OPEN_READ_FAILED;
srcFileWasOpened = true;
/* UPDATE the file's size, in case it's changed since we added it */
struct stat statStruct;
rc = stat(srcFileInTree->pathAndName, &statStruct);
if(rc != 0)
return BKERROR_STAT_FAILED;
srcFileInTree->size = statStruct.st_size;
/* UPDATE the file's size, in case it's changed since we added it */
}
if(nameToUse == NULL)
destPathAndName = malloc(strlen(destDir) +
strlen(BK_BASE_PTR(srcFileInTree)->name) + 2);
else
destPathAndName = malloc(strlen(destDir) + strlen(nameToUse) + 2);
if(destPathAndName == NULL)
{
if(srcFileWasOpened)
close(srcFile);
return BKERROR_OUT_OF_MEMORY;
}
strcpy(destPathAndName, destDir);
if(destDir[strlen(destDir) - 1] != '/')
strcat(destPathAndName, "/");
if(nameToUse == NULL)
strcat(destPathAndName, BK_BASE_PTR(srcFileInTree)->name);
else
strcat(destPathAndName, nameToUse);
if(access(destPathAndName, F_OK) == 0)
{
if(srcFileWasOpened)
close(srcFile);
free(destPathAndName);
return BKERROR_DUPLICATE_EXTRACT;
}
/* WRITE file */
if(keepPermissions)
destFilePerms = BK_BASE_PTR(srcFileInTree)->posixFileMode;
else
destFilePerms = volInfo->posixFileDefaults;
destFile = open(destPathAndName, O_WRONLY | O_CREAT | O_TRUNC, destFilePerms);
if(destFile == -1)
{
if(srcFileWasOpened)
close(srcFile);
free(destPathAndName);
return BKERROR_OPEN_WRITE_FAILED;
}
free(destPathAndName);
rc = copyByteBlock(volInfo, srcFile, destFile, srcFileInTree->size);
if(rc < 0)
{
close(destFile);
if(srcFileWasOpened)
close(srcFile);
return rc;
}
close(destFile);
if(destFile == -1)
{
if(srcFileWasOpened)
close(srcFile);
return BKERROR_EXOTIC;
}
/* END WRITE file */
if(srcFileWasOpened)
close(srcFile);
return 1;
}
int extractSymlink(BkSymLink* srcLink, const char* destDir,
const char* nameToUse)
{
char* destPathAndName;
int rc;
if(nameToUse == NULL)
destPathAndName = malloc(strlen(destDir) +
strlen(BK_BASE_PTR(srcLink)->name) + 2);
else
destPathAndName = malloc(strlen(destDir) + strlen(nameToUse) + 2);
if(destPathAndName == NULL)
return BKERROR_OUT_OF_MEMORY;
strcpy(destPathAndName, destDir);
if(destDir[strlen(destDir) - 1] != '/')
strcat(destPathAndName, "/");
if(nameToUse == NULL)
strcat(destPathAndName, BK_BASE_PTR(srcLink)->name);
else
strcat(destPathAndName, nameToUse);
if(access(destPathAndName, F_OK) == 0)
{
free(destPathAndName);
return BKERROR_DUPLICATE_EXTRACT;
}
rc = symlink(srcLink->target, destPathAndName);
if(rc == -1)
{
free(destPathAndName);
return BKERROR_CREATE_SYMLINK_FAILED;
}
free(destPathAndName);
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1