Add package extract -i option

Allows to specify an alternate location for the .PackageInfo.
This commit is contained in:
Ingo Weinhold 2011-07-01 02:21:28 +02:00
parent 55191c9ac9
commit 4f5d405e34
2 changed files with 80 additions and 32 deletions

View File

@ -43,6 +43,8 @@ struct PackageContentExtractHandler : BPackageContentHandler {
fPackageFileReader(packageFileFD), fPackageFileReader(packageFileFD),
fDataBuffer(NULL), fDataBuffer(NULL),
fDataBufferSize(0), fDataBufferSize(0),
fBaseDirectory(AT_FDCWD),
fInfoFileName(NULL),
fErrorOccurred(false) fErrorOccurred(false)
{ {
} }
@ -66,6 +68,16 @@ struct PackageContentExtractHandler : BPackageContentHandler {
return B_OK; return B_OK;
} }
void SetBaseDirectory(int fd)
{
fBaseDirectory = fd;
}
void SetPackageInfoFile(const char* infoFileName)
{
fInfoFileName = infoFileName;
}
virtual status_t HandleEntry(BPackageEntry* entry) virtual status_t HandleEntry(BPackageEntry* entry)
{ {
// create a token // create a token
@ -74,14 +86,14 @@ struct PackageContentExtractHandler : BPackageContentHandler {
return B_NO_MEMORY; return B_NO_MEMORY;
ObjectDeleter<Token> tokenDeleter(token); ObjectDeleter<Token> tokenDeleter(token);
// get parent FD // get parent FD and the entry name
int parentFD = AT_FDCWD; int parentFD;
if (entry->Parent() != NULL) const char* entryName;
parentFD = ((Token*)entry->Parent()->UserToken())->fd; _GetParentFDAndEntryName(entry, parentFD, entryName);
// check whether something is in the way // check whether something is in the way
struct stat st; struct stat st;
bool entryExists = fstatat(parentFD, entry->Name(), &st, bool entryExists = fstatat(parentFD, entryName, &st,
AT_SYMLINK_NOFOLLOW) == 0; AT_SYMLINK_NOFOLLOW) == 0;
if (entryExists) { if (entryExists) {
if (S_ISREG(entry->Mode()) || S_ISLNK(entry->Mode())) { if (S_ISREG(entry->Mode()) || S_ISLNK(entry->Mode())) {
@ -89,13 +101,13 @@ struct PackageContentExtractHandler : BPackageContentHandler {
// remove it, otherwise fail. // remove it, otherwise fail.
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
fprintf(stderr, "Error: Can't create entry \"%s\", since " fprintf(stderr, "Error: Can't create entry \"%s\", since "
"something is in the way\n", entry->Name()); "something is in the way\n", entryName);
return B_FILE_EXISTS; return B_FILE_EXISTS;
} }
if (unlinkat(parentFD, entry->Name(), 0) != 0) { if (unlinkat(parentFD, entryName, 0) != 0) {
fprintf(stderr, "Error: Failed to unlink entry \"%s\": %s\n", fprintf(stderr, "Error: Failed to unlink entry \"%s\": %s\n",
entry->Name(), strerror(errno)); entryName, strerror(errno));
return errno; return errno;
} }
@ -105,7 +117,7 @@ struct PackageContentExtractHandler : BPackageContentHandler {
// fail. // fail.
if (!S_ISDIR(st.st_mode)) { if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "Error: Can't create directory \"%s\", " fprintf(stderr, "Error: Can't create directory \"%s\", "
"since something is in the way\n", entry->Name()); "since something is in the way\n", entryName);
return B_FILE_EXISTS; return B_FILE_EXISTS;
} }
} }
@ -115,14 +127,14 @@ struct PackageContentExtractHandler : BPackageContentHandler {
int fd = -1; int fd = -1;
if (S_ISREG(entry->Mode())) { if (S_ISREG(entry->Mode())) {
// create the file // create the file
fd = openat(parentFD, entry->Name(), O_RDWR | O_CREAT | O_EXCL, fd = openat(parentFD, entryName, O_RDWR | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR); S_IRUSR | S_IWUSR);
// Note: We use read+write user permissions now -- so write // Note: We use read+write user permissions now -- so write
// operations (e.g. attributes) won't fail, but set them to the // operations (e.g. attributes) won't fail, but set them to the
// desired ones in HandleEntryDone(). // desired ones in HandleEntryDone().
if (fd < 0) { if (fd < 0) {
fprintf(stderr, "Error: Failed to create file \"%s\": %s\n", fprintf(stderr, "Error: Failed to create file \"%s\": %s\n",
entry->Name(), strerror(errno)); entryName, strerror(errno));
return errno; return errno;
} }
@ -142,35 +154,35 @@ struct PackageContentExtractHandler : BPackageContentHandler {
// create the symlink // create the symlink
const char* symlinkPath = entry->SymlinkPath(); const char* symlinkPath = entry->SymlinkPath();
if (symlinkat(symlinkPath != NULL ? symlinkPath : "", parentFD, if (symlinkat(symlinkPath != NULL ? symlinkPath : "", parentFD,
entry->Name()) != 0) { entryName) != 0) {
fprintf(stderr, "Error: Failed to create symlink \"%s\": %s\n", fprintf(stderr, "Error: Failed to create symlink \"%s\": %s\n",
entry->Name(), strerror(errno)); entryName, strerror(errno));
return errno; return errno;
} }
// TODO: Set symlink permissions? // TODO: Set symlink permissions?
} else if (S_ISDIR(entry->Mode())) { } else if (S_ISDIR(entry->Mode())) {
// create the directory, if necessary // create the directory, if necessary
if (!entryExists if (!entryExists
&& mkdirat(parentFD, entry->Name(), S_IRWXU) != 0) { && mkdirat(parentFD, entryName, S_IRWXU) != 0) {
// Note: We use read+write+exec user permissions now -- so write // Note: We use read+write+exec user permissions now -- so write
// operations (e.g. attributes) won't fail, but set them to the // operations (e.g. attributes) won't fail, but set them to the
// desired ones in HandleEntryDone(). // desired ones in HandleEntryDone().
fprintf(stderr, "Error: Failed to create directory \"%s\": " fprintf(stderr, "Error: Failed to create directory \"%s\": "
"%s\n", entry->Name(), strerror(errno)); "%s\n", entryName, strerror(errno));
return errno; return errno;
} }
} else { } else {
fprintf(stderr, "Error: Invalid file type for entry \"%s\"\n", fprintf(stderr, "Error: Invalid file type for entry \"%s\"\n",
entry->Name()); entryName);
return B_BAD_DATA; return B_BAD_DATA;
} }
// If not done yet (symlink, dir), open the node -- we need the FD. // If not done yet (symlink, dir), open the node -- we need the FD.
if (fd < 0) { if (fd < 0) {
fd = openat(parentFD, entry->Name(), O_RDONLY | O_NOTRAVERSE); fd = openat(parentFD, entryName, O_RDONLY | O_NOTRAVERSE);
if (fd < 0) { if (fd < 0) {
fprintf(stderr, "Error: Failed to open entry \"%s\": %s\n", fprintf(stderr, "Error: Failed to open entry \"%s\": %s\n",
entry->Name(), strerror(errno)); entryName, strerror(errno));
return errno; return errno;
} }
} }
@ -198,10 +210,14 @@ struct PackageContentExtractHandler : BPackageContentHandler {
int fd = fs_fopen_attr(entryFD, attribute->Name(), attribute->Type(), int fd = fs_fopen_attr(entryFD, attribute->Name(), attribute->Type(),
O_WRONLY | O_CREAT | O_TRUNC); O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0) { if (fd < 0) {
fprintf(stderr, "Error: Failed to create attribute \"%s\" of " int parentFD;
"file \"%s\": %s\n", attribute->Name(), entry->Name(), const char* entryName;
strerror(errno)); _GetParentFDAndEntryName(entry, parentFD, entryName);
return errno;
fprintf(stderr, "Error: Failed to create attribute \"%s\" of "
"file \"%s\": %s\n", attribute->Name(), entryName,
strerror(errno));
return errno;
} }
// write data // write data
@ -223,15 +239,15 @@ struct PackageContentExtractHandler : BPackageContentHandler {
{ {
// set the node permissions for non-symlinks // set the node permissions for non-symlinks
if (!S_ISLNK(entry->Mode())) { if (!S_ISLNK(entry->Mode())) {
// get parent FD // get parent FD and entry name
int parentFD = AT_FDCWD; int parentFD;
if (entry->Parent() != NULL) const char* entryName;
parentFD = ((Token*)entry->Parent()->UserToken())->fd; _GetParentFDAndEntryName(entry, parentFD, entryName);
if (fchmodat(parentFD, entry->Name(), entry->Mode() & ALLPERMS, if (fchmodat(parentFD, entryName, entry->Mode() & ALLPERMS,
/*AT_SYMLINK_NOFOLLOW*/0) != 0) { /*AT_SYMLINK_NOFOLLOW*/0) != 0) {
fprintf(stderr, "Warning: Failed to set permissions of file " fprintf(stderr, "Warning: Failed to set permissions of file "
"\"%s\": %s\n", entry->Name(), strerror(errno)); "\"%s\": %s\n", entryName, strerror(errno));
} }
} }
@ -272,6 +288,21 @@ private:
}; };
private: private:
void _GetParentFDAndEntryName(BPackageEntry* entry, int& _parentFD,
const char*& _entryName)
{
_entryName = entry->Name();
if (fInfoFileName != NULL
&& strcmp(_entryName, B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
_parentFD = AT_FDCWD;
_entryName = fInfoFileName;
} else {
_parentFD = entry->Parent() != NULL
? ((Token*)entry->Parent()->UserToken())->fd : fBaseDirectory;
}
}
status_t _ExtractFileData(BDataReader* dataReader, const BPackageData& data, status_t _ExtractFileData(BDataReader* dataReader, const BPackageData& data,
int fd) int fd)
{ {
@ -321,6 +352,8 @@ private:
BFDDataReader fPackageFileReader; BFDDataReader fPackageFileReader;
void* fDataBuffer; void* fDataBuffer;
size_t fDataBufferSize; size_t fDataBufferSize;
int fBaseDirectory;
const char* fInfoFileName;
bool fErrorOccurred; bool fErrorOccurred;
}; };
@ -329,6 +362,7 @@ int
command_extract(int argc, const char* const* argv) command_extract(int argc, const char* const* argv)
{ {
const char* changeToDirectory = NULL; const char* changeToDirectory = NULL;
const char* packageInfoFileName = NULL;
while (true) { while (true) {
static struct option sLongOptions[] = { static struct option sLongOptions[] = {
@ -337,7 +371,7 @@ command_extract(int argc, const char* const* argv)
}; };
opterr = 0; // don't print errors opterr = 0; // don't print errors
int c = getopt_long(argc, (char**)argv, "+C:h", sLongOptions, NULL); int c = getopt_long(argc, (char**)argv, "+C:hi:", sLongOptions, NULL);
if (c == -1) if (c == -1)
break; break;
@ -350,6 +384,10 @@ command_extract(int argc, const char* const* argv)
print_usage_and_exit(false); print_usage_and_exit(false);
break; break;
case 'i':
packageInfoFileName = optarg;
break;
default: default:
print_usage_and_exit(true); print_usage_and_exit(true);
break; break;
@ -369,17 +407,26 @@ command_extract(int argc, const char* const* argv)
if (error != B_OK) if (error != B_OK)
return 1; return 1;
// change directory, if requested PackageContentExtractHandler handler(packageReader.PackageFileFD());
// get the target directory, if requested
if (changeToDirectory != NULL) { if (changeToDirectory != NULL) {
if (chdir(changeToDirectory) != 0) { int currentDirFD = open(changeToDirectory, O_RDONLY);
if (currentDirFD < 0) {
fprintf(stderr, "Error: Failed to change the current working " fprintf(stderr, "Error: Failed to change the current working "
"directory to \"%s\": %s\n", changeToDirectory, "directory to \"%s\": %s\n", changeToDirectory,
strerror(errno)); strerror(errno));
return 1;
} }
handler.SetBaseDirectory(currentDirFD);
} }
// If a package info file name is given, set it.
if (packageInfoFileName != NULL)
handler.SetPackageInfoFile(packageInfoFileName);
// extract // extract
PackageContentExtractHandler handler(packageReader.PackageFileFD());
error = handler.Init(); error = handler.Init();
if (error != B_OK) if (error != B_OK)
return 1; return 1;

View File

@ -42,6 +42,7 @@ static const char* kUsage =
" -C <dir> - Change to directory <dir> before extracting the " " -C <dir> - Change to directory <dir> before extracting the "
"contents\n" "contents\n"
" of the archive.\n" " of the archive.\n"
" -i <info> - Extract the .PackageInfo file to <info> instead.\n"
"\n" "\n"
" list [ <options> ] <package>\n" " list [ <options> ] <package>\n"
" Lists the contents of package file <package>.\n" " Lists the contents of package file <package>.\n"