mirror of
https://review.haiku-os.org/haiku
synced 2025-02-01 03:06:08 +01:00
* 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:
parent
5519ed578c
commit
dcb3c6c321
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2005, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved.
|
||||
* Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de.
|
||||
* Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
@ -33,8 +33,8 @@
|
||||
#endif
|
||||
|
||||
|
||||
static const uint32 kCompressedArchiveOffset = 192 * 1024; // at 192 kB
|
||||
static const size_t kTarRegionSize = 4 * 1024 * 1024; // 4 MB
|
||||
static const uint32 kFloppyArchiveOffset = 192 * 1024; // at 192 kB
|
||||
static const size_t kTarRegionSize = 32 * 1024 * 1024; // 32 MB
|
||||
|
||||
namespace TarFS {
|
||||
|
||||
@ -126,6 +126,31 @@ class Directory : public ::Directory, public Entry {
|
||||
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 {
|
||||
public:
|
||||
Volume();
|
||||
@ -134,6 +159,11 @@ class Volume : public TarFS::Directory {
|
||||
status_t Init(boot::Partition *partition);
|
||||
|
||||
TarFS::Directory *Root() { return this; }
|
||||
|
||||
private:
|
||||
status_t _Inflate(boot::Partition *partition, void* cookie,
|
||||
off_t offset, RegionDeleter& regionDeleter,
|
||||
size_t* inflatedBytes);
|
||||
};
|
||||
|
||||
} // namespace TarFS
|
||||
@ -453,12 +483,19 @@ TarFS::Directory::AddFile(tar_header *header)
|
||||
return error;
|
||||
}
|
||||
|
||||
// create the file
|
||||
TarFS::File *file = new(nothrow) TarFS::File(header, leaf);
|
||||
if (!file)
|
||||
// create the entry
|
||||
TarFS::Entry *entry;
|
||||
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;
|
||||
|
||||
dir->fEntries.Add(file);
|
||||
dir->fEntries.Add(entry);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@ -481,6 +518,83 @@ TarFS::Directory::Inode() const
|
||||
// #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::Directory("Boot from CD-ROM")
|
||||
{
|
||||
@ -516,79 +630,22 @@ TarFS::Volume::Init(boot::Partition *partition)
|
||||
}
|
||||
} _(partition, cookie);
|
||||
|
||||
// inflate the tar file -- try offset 0 and the archive offset on a floppy
|
||||
// disk
|
||||
RegionDeleter regionDeleter;
|
||||
|
||||
char *out = NULL;
|
||||
|
||||
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;
|
||||
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;
|
||||
size_t inflatedBytes;
|
||||
status_t status = _Inflate(partition, cookie, 0, regionDeleter,
|
||||
&inflatedBytes);
|
||||
if (status != B_OK) {
|
||||
status = _Inflate(partition, cookie, kFloppyArchiveOffset,
|
||||
regionDeleter, &inflatedBytes);
|
||||
}
|
||||
|
||||
status = B_OK;
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// parse the tar file
|
||||
char *block = out;
|
||||
int blockCount = zStream.total_out / BLOCK_SIZE;
|
||||
char *block = (char*)regionDeleter.Get();
|
||||
int blockCount = inflatedBytes / BLOCK_SIZE;
|
||||
int blockIndex = 0;
|
||||
|
||||
while (blockIndex < blockCount) {
|
||||
@ -615,6 +672,7 @@ TarFS::Volume::Init(boot::Partition *partition)
|
||||
switch (header->type) {
|
||||
case TAR_FILE:
|
||||
case TAR_FILE2:
|
||||
case TAR_SYMLINK:
|
||||
status = AddFile(header);
|
||||
break;
|
||||
|
||||
@ -626,6 +684,7 @@ TarFS::Volume::Init(boot::Partition *partition)
|
||||
// this is a long file name
|
||||
// TODO: read long name
|
||||
default:
|
||||
dprintf("tarfs: unsupported file type: %d ('%c')\n", header->type, header->type);
|
||||
// unsupported type
|
||||
status = B_ERROR;
|
||||
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 -
|
||||
|
||||
|
||||
|
@ -38,6 +38,7 @@ static const char *kOldTarHeaderMagic = "ustar ";
|
||||
enum {
|
||||
TAR_FILE = '0',
|
||||
TAR_FILE2 = '\0',
|
||||
TAR_SYMLINK = '2',
|
||||
TAR_DIRECTORY = '5',
|
||||
TAR_LONG_NAME = 'L',
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user