* Added support for symlinks.
* Prepared for other uses than just boot floppies (/CDs). We first try to inflate the contents of the given device at offset 0 before using the floppy disk offset. This will make it easy to mount tgz files loaded via network or built into the boot loader itself. * Increased the max possible size of inflated data to 32 MB. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21583 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2005, Ingo Weinhold, [email protected]. All rights reserved.
|
* Copyright 2005-2007, Ingo Weinhold, [email protected].
|
||||||
* Copyright 2005, Axel Dörfler, [email protected]. All rights reserved.
|
* Copyright 2005, Axel Dörfler, [email protected]. All rights reserved.
|
||||||
*
|
*
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
@@ -33,8 +33,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static const uint32 kCompressedArchiveOffset = 192 * 1024; // at 192 kB
|
static const uint32 kFloppyArchiveOffset = 192 * 1024; // at 192 kB
|
||||||
static const size_t kTarRegionSize = 4 * 1024 * 1024; // 4 MB
|
static const size_t kTarRegionSize = 32 * 1024 * 1024; // 32 MB
|
||||||
|
|
||||||
namespace TarFS {
|
namespace TarFS {
|
||||||
|
|
||||||
@@ -126,6 +126,31 @@ class Directory : public ::Directory, public Entry {
|
|||||||
EntryList fEntries;
|
EntryList fEntries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Symlink : public ::Node, public Entry {
|
||||||
|
public:
|
||||||
|
Symlink(tar_header *header, const char *name);
|
||||||
|
virtual ~Symlink();
|
||||||
|
|
||||||
|
virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
|
||||||
|
size_t bufferSize);
|
||||||
|
virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
|
||||||
|
size_t bufferSize);
|
||||||
|
|
||||||
|
virtual status_t GetName(char *nameBuffer, size_t bufferSize) const;
|
||||||
|
|
||||||
|
virtual int32 Type() const;
|
||||||
|
virtual off_t Size() const;
|
||||||
|
virtual ino_t Inode() const;
|
||||||
|
|
||||||
|
virtual ::Node *ToNode() { return this; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
tar_header *fHeader;
|
||||||
|
size_t fSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class Volume : public TarFS::Directory {
|
class Volume : public TarFS::Directory {
|
||||||
public:
|
public:
|
||||||
Volume();
|
Volume();
|
||||||
@@ -134,6 +159,11 @@ class Volume : public TarFS::Directory {
|
|||||||
status_t Init(boot::Partition *partition);
|
status_t Init(boot::Partition *partition);
|
||||||
|
|
||||||
TarFS::Directory *Root() { return this; }
|
TarFS::Directory *Root() { return this; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _Inflate(boot::Partition *partition, void* cookie,
|
||||||
|
off_t offset, RegionDeleter& regionDeleter,
|
||||||
|
size_t* inflatedBytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace TarFS
|
} // namespace TarFS
|
||||||
@@ -453,12 +483,19 @@ TarFS::Directory::AddFile(tar_header *header)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the file
|
// create the entry
|
||||||
TarFS::File *file = new(nothrow) TarFS::File(header, leaf);
|
TarFS::Entry *entry;
|
||||||
if (!file)
|
if (header->type == TAR_FILE || header->type == TAR_FILE2)
|
||||||
|
entry = new(nothrow) TarFS::File(header, leaf);
|
||||||
|
else if (header->type == TAR_SYMLINK)
|
||||||
|
entry = new(nothrow) TarFS::Symlink(header, leaf);
|
||||||
|
else
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
if (!entry)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
dir->fEntries.Add(file);
|
dir->fEntries.Add(entry);
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -481,6 +518,83 @@ TarFS::Directory::Inode() const
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
TarFS::Symlink::Symlink(tar_header *header, const char *name)
|
||||||
|
: TarFS::Entry(name),
|
||||||
|
fHeader(header)
|
||||||
|
{
|
||||||
|
fSize = strnlen(header->linkname, sizeof(header->linkname));
|
||||||
|
// null-terminate for sure (might overwrite a byte of the magic)
|
||||||
|
header->linkname[fSize++] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TarFS::Symlink::~Symlink()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
TarFS::Symlink::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
|
||||||
|
{
|
||||||
|
TRACE(("tarfs: symlink read at %Ld, %lu bytes, fSize = %Ld\n", pos,
|
||||||
|
bufferSize, fSize));
|
||||||
|
|
||||||
|
if (pos < 0 || !buffer)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
if (pos >= fSize || bufferSize == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t toRead = fSize - pos;
|
||||||
|
if (toRead > bufferSize)
|
||||||
|
toRead = bufferSize;
|
||||||
|
|
||||||
|
memcpy(buffer, fHeader->linkname + pos, toRead);
|
||||||
|
|
||||||
|
return toRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
TarFS::Symlink::WriteAt(void *cookie, off_t pos, const void *buffer,
|
||||||
|
size_t bufferSize)
|
||||||
|
{
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
TarFS::Symlink::GetName(char *nameBuffer, size_t bufferSize) const
|
||||||
|
{
|
||||||
|
return strlcpy(nameBuffer, Name(), bufferSize) >= bufferSize
|
||||||
|
? B_BUFFER_OVERFLOW : B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
TarFS::Symlink::Type() const
|
||||||
|
{
|
||||||
|
return S_IFLNK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
off_t
|
||||||
|
TarFS::Symlink::Size() const
|
||||||
|
{
|
||||||
|
return fSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ino_t
|
||||||
|
TarFS::Symlink::Inode() const
|
||||||
|
{
|
||||||
|
return fID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
TarFS::Volume::Volume()
|
TarFS::Volume::Volume()
|
||||||
: TarFS::Directory("Boot from CD-ROM")
|
: TarFS::Directory("Boot from CD-ROM")
|
||||||
{
|
{
|
||||||
@@ -516,79 +630,22 @@ TarFS::Volume::Init(boot::Partition *partition)
|
|||||||
}
|
}
|
||||||
} _(partition, cookie);
|
} _(partition, cookie);
|
||||||
|
|
||||||
|
// inflate the tar file -- try offset 0 and the archive offset on a floppy
|
||||||
|
// disk
|
||||||
RegionDeleter regionDeleter;
|
RegionDeleter regionDeleter;
|
||||||
|
size_t inflatedBytes;
|
||||||
char *out = NULL;
|
status_t status = _Inflate(partition, cookie, 0, regionDeleter,
|
||||||
|
&inflatedBytes);
|
||||||
char in[2048];
|
if (status != B_OK) {
|
||||||
z_stream zStream = {
|
status = _Inflate(partition, cookie, kFloppyArchiveOffset,
|
||||||
(Bytef*)in, // next in
|
regionDeleter, &inflatedBytes);
|
||||||
sizeof(in), // avail in
|
|
||||||
0, // total in
|
|
||||||
NULL, // next out
|
|
||||||
0, // avail out
|
|
||||||
0, // total out
|
|
||||||
0, // msg
|
|
||||||
0, // state
|
|
||||||
Z_NULL, // zalloc
|
|
||||||
Z_NULL, // zfree
|
|
||||||
Z_NULL, // opaque
|
|
||||||
0, // data type
|
|
||||||
0, // adler
|
|
||||||
0, // reserved
|
|
||||||
};
|
|
||||||
|
|
||||||
int status;
|
|
||||||
uint32 offset = kCompressedArchiveOffset;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (partition->ReadAt(cookie, offset, in, sizeof(in)) != sizeof(in)) {
|
|
||||||
status = Z_STREAM_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
zStream.avail_in = sizeof(in);
|
|
||||||
zStream.next_in = (Bytef *)in;
|
|
||||||
|
|
||||||
if (offset == kCompressedArchiveOffset) {
|
|
||||||
// check and skip gzip header
|
|
||||||
if (!skip_gzip_header(&zStream))
|
|
||||||
return B_BAD_DATA;
|
|
||||||
|
|
||||||
if (platform_allocate_region((void **)&out, kTarRegionSize,
|
|
||||||
B_READ_AREA | B_WRITE_AREA, false) != B_OK) {
|
|
||||||
TRACE(("tarfs: allocating region failed!\n"));
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
regionDeleter.SetTo(out);
|
|
||||||
zStream.avail_out = kTarRegionSize;
|
|
||||||
zStream.next_out = (Bytef *)out;
|
|
||||||
|
|
||||||
status = inflateInit2(&zStream, -15);
|
|
||||||
if (status != Z_OK)
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = inflate(&zStream, Z_SYNC_FLUSH);
|
|
||||||
offset += sizeof(in);
|
|
||||||
|
|
||||||
if (zStream.avail_in != 0 && status != Z_STREAM_END)
|
|
||||||
dprintf("tarfs: didn't read whole block!\n");
|
|
||||||
} while (status == Z_OK);
|
|
||||||
|
|
||||||
inflateEnd(&zStream);
|
|
||||||
|
|
||||||
if (status != Z_STREAM_END) {
|
|
||||||
TRACE(("tarfs: inflating failed: %d!\n", status));
|
|
||||||
return B_BAD_DATA;
|
|
||||||
}
|
}
|
||||||
|
if (status != B_OK)
|
||||||
status = B_OK;
|
return status;
|
||||||
|
|
||||||
// parse the tar file
|
// parse the tar file
|
||||||
char *block = out;
|
char *block = (char*)regionDeleter.Get();
|
||||||
int blockCount = zStream.total_out / BLOCK_SIZE;
|
int blockCount = inflatedBytes / BLOCK_SIZE;
|
||||||
int blockIndex = 0;
|
int blockIndex = 0;
|
||||||
|
|
||||||
while (blockIndex < blockCount) {
|
while (blockIndex < blockCount) {
|
||||||
@@ -615,6 +672,7 @@ TarFS::Volume::Init(boot::Partition *partition)
|
|||||||
switch (header->type) {
|
switch (header->type) {
|
||||||
case TAR_FILE:
|
case TAR_FILE:
|
||||||
case TAR_FILE2:
|
case TAR_FILE2:
|
||||||
|
case TAR_SYMLINK:
|
||||||
status = AddFile(header);
|
status = AddFile(header);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -626,6 +684,7 @@ TarFS::Volume::Init(boot::Partition *partition)
|
|||||||
// this is a long file name
|
// this is a long file name
|
||||||
// TODO: read long name
|
// TODO: read long name
|
||||||
default:
|
default:
|
||||||
|
dprintf("tarfs: unsupported file type: %d ('%c')\n", header->type, header->type);
|
||||||
// unsupported type
|
// unsupported type
|
||||||
status = B_ERROR;
|
status = B_ERROR;
|
||||||
break;
|
break;
|
||||||
@@ -646,6 +705,89 @@ TarFS::Volume::Init(boot::Partition *partition)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
TarFS::Volume::_Inflate(boot::Partition *partition, void* cookie, off_t offset,
|
||||||
|
RegionDeleter& regionDeleter, size_t* inflatedBytes)
|
||||||
|
{
|
||||||
|
char in[2048];
|
||||||
|
z_stream zStream = {
|
||||||
|
(Bytef*)in, // next in
|
||||||
|
sizeof(in), // avail in
|
||||||
|
0, // total in
|
||||||
|
NULL, // next out
|
||||||
|
0, // avail out
|
||||||
|
0, // total out
|
||||||
|
0, // msg
|
||||||
|
0, // state
|
||||||
|
Z_NULL, // zalloc
|
||||||
|
Z_NULL, // zfree
|
||||||
|
Z_NULL, // opaque
|
||||||
|
0, // data type
|
||||||
|
0, // adler
|
||||||
|
0, // reserved
|
||||||
|
};
|
||||||
|
|
||||||
|
int status;
|
||||||
|
char* out = (char*)regionDeleter.Get();
|
||||||
|
bool headerRead = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ssize_t bytesRead = partition->ReadAt(cookie, offset, in, sizeof(in));
|
||||||
|
if (bytesRead != (ssize_t)sizeof(in)) {
|
||||||
|
if (bytesRead <= 0) {
|
||||||
|
status = Z_STREAM_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zStream.avail_in = bytesRead;
|
||||||
|
zStream.next_in = (Bytef *)in;
|
||||||
|
|
||||||
|
if (!headerRead) {
|
||||||
|
// check and skip gzip header
|
||||||
|
if (!skip_gzip_header(&zStream))
|
||||||
|
return B_BAD_DATA;
|
||||||
|
headerRead = true;
|
||||||
|
|
||||||
|
if (!out) {
|
||||||
|
// allocate memory for the uncompressed data
|
||||||
|
if (platform_allocate_region((void **)&out, kTarRegionSize,
|
||||||
|
B_READ_AREA | B_WRITE_AREA, false) != B_OK) {
|
||||||
|
TRACE(("tarfs: allocating region failed!\n"));
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
regionDeleter.SetTo(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
zStream.avail_out = kTarRegionSize;
|
||||||
|
zStream.next_out = (Bytef *)out;
|
||||||
|
|
||||||
|
status = inflateInit2(&zStream, -15);
|
||||||
|
if (status != Z_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = inflate(&zStream, Z_SYNC_FLUSH);
|
||||||
|
offset += bytesRead;
|
||||||
|
|
||||||
|
if (zStream.avail_in != 0 && status != Z_STREAM_END)
|
||||||
|
dprintf("tarfs: didn't read whole block!\n");
|
||||||
|
} while (status == Z_OK);
|
||||||
|
|
||||||
|
inflateEnd(&zStream);
|
||||||
|
|
||||||
|
if (status != Z_STREAM_END) {
|
||||||
|
TRACE(("tarfs: inflating failed: %d!\n", status));
|
||||||
|
return B_BAD_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
*inflatedBytes = zStream.total_out;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ static const char *kOldTarHeaderMagic = "ustar ";
|
|||||||
enum {
|
enum {
|
||||||
TAR_FILE = '0',
|
TAR_FILE = '0',
|
||||||
TAR_FILE2 = '\0',
|
TAR_FILE2 = '\0',
|
||||||
|
TAR_SYMLINK = '2',
|
||||||
TAR_DIRECTORY = '5',
|
TAR_DIRECTORY = '5',
|
||||||
TAR_LONG_NAME = 'L',
|
TAR_LONG_NAME = 'L',
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user