1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "PackageFile.h"
8
9#include <algorithm>
10#include <new>
11
12#include <fs_cache.h>
13#include <util/AutoLock.h>
14
15#include <package/hpkg/DataOutput.h>
16#include <package/hpkg/DataReader.h>
17#include <package/hpkg/PackageDataReader.h>
18
19#include "DebugSupport.h"
20#include "GlobalFactory.h"
21#include "Package.h"
22
23
24using namespace BPackageKit::BHPKG;
25
26
27// #pragma mark - DataAccessor
28
29
30struct PackageFile::IORequestOutput : BDataOutput {
31public:
32	IORequestOutput(io_request* request)
33		:
34		fRequest(request)
35	{
36	}
37
38	virtual status_t WriteData(const void* buffer, size_t size)
39	{
40		RETURN_ERROR(write_to_io_request(fRequest, buffer, size));
41	}
42
43private:
44	io_request*	fRequest;
45};
46
47
48struct PackageFile::DataAccessor {
49	DataAccessor(BPackageData* data)
50		:
51		fData(data),
52		fDataReader(NULL),
53		fReader(NULL),
54		fFileCache(NULL)
55	{
56		mutex_init(&fLock, "file data accessor");
57	}
58
59	~DataAccessor()
60	{
61		file_cache_delete(fFileCache);
62		delete fReader;
63		delete fDataReader;
64		mutex_destroy(&fLock);
65	}
66
67	status_t Init(dev_t deviceID, ino_t nodeID, int fd)
68	{
69		// create a BDataReader for the compressed data
70		if (fData->IsEncodedInline()) {
71			fDataReader = new(std::nothrow) BBufferDataReader(
72				fData->InlineData(), fData->CompressedSize());
73		} else
74			fDataReader = new(std::nothrow) BFDDataReader(fd);
75
76		if (fDataReader == NULL)
77			RETURN_ERROR(B_NO_MEMORY);
78
79		// create a BPackageDataReader
80		status_t error = GlobalFactory::Default()->CreatePackageDataReader(
81			fDataReader, *fData, fReader);
82		if (error != B_OK)
83			RETURN_ERROR(error);
84
85		// create a file cache
86		fFileCache = file_cache_create(deviceID, nodeID,
87			fData->UncompressedSize());
88		if (fFileCache == NULL)
89			RETURN_ERROR(B_NO_MEMORY);
90
91		return B_OK;
92	}
93
94	status_t ReadData(off_t offset, void* buffer, size_t* bufferSize)
95	{
96		if (offset < 0 || (uint64)offset > fData->UncompressedSize())
97			return B_BAD_VALUE;
98
99		*bufferSize = std::min((uint64)*bufferSize,
100			fData->UncompressedSize() - offset);
101
102		return file_cache_read(fFileCache, NULL, offset, buffer, bufferSize);
103	}
104
105	status_t ReadData(io_request* request)
106	{
107		off_t offset = io_request_offset(request);
108		size_t size = io_request_length(request);
109
110		if (offset < 0 || (uint64)offset > fData->UncompressedSize())
111			RETURN_ERROR(B_BAD_VALUE);
112
113		size_t toRead = std::min((uint64)size,
114			fData->UncompressedSize() - offset);
115
116		if (toRead > 0) {
117			IORequestOutput output(request);
118			MutexLocker locker(fLock);
119			status_t error = fReader->ReadDataToOutput(offset, toRead, &output);
120			if (error != B_OK)
121				RETURN_ERROR(error);
122		}
123
124		return B_OK;
125	}
126
127private:
128	mutex				fLock;
129	BPackageData*		fData;
130	BDataReader*			fDataReader;
131	BPackageDataReader*	fReader;
132	void*				fFileCache;
133};
134
135
136// #pragma mark - PackageFile
137
138
139PackageFile::PackageFile(Package* package, mode_t mode, const BPackageData& data)
140	:
141	PackageLeafNode(package, mode),
142	fData(data),
143	fDataAccessor(NULL)
144{
145}
146
147
148PackageFile::~PackageFile()
149{
150}
151
152
153status_t
154PackageFile::VFSInit(dev_t deviceID, ino_t nodeID)
155{
156	// open the package
157	int fd = fPackage->Open();
158	if (fd < 0)
159		RETURN_ERROR(fd);
160	PackageCloser packageCloser(fPackage);
161
162	// create the data accessor
163	fDataAccessor = new(std::nothrow) DataAccessor(&fData);
164	if (fDataAccessor == NULL)
165		RETURN_ERROR(B_NO_MEMORY);
166
167	status_t error = fDataAccessor->Init(deviceID, nodeID, fd);
168	if (error != B_OK) {
169		delete fDataAccessor;
170		fDataAccessor = NULL;
171		return error;
172	}
173
174	packageCloser.Detach();
175	return B_OK;
176}
177
178
179void
180PackageFile::VFSUninit()
181{
182	if (fDataAccessor != NULL) {
183		fPackage->Close();
184		delete fDataAccessor;
185		fDataAccessor = NULL;
186	}
187}
188
189
190off_t
191PackageFile::FileSize() const
192{
193	return fData.UncompressedSize();
194}
195
196
197status_t
198PackageFile::Read(off_t offset, void* buffer, size_t* bufferSize)
199{
200	if (fDataAccessor == NULL)
201		return B_BAD_VALUE;
202	return fDataAccessor->ReadData(offset, buffer, bufferSize);
203}
204
205
206status_t
207PackageFile::Read(io_request* request)
208{
209	if (fDataAccessor == NULL)
210		return B_BAD_VALUE;
211	return fDataAccessor->ReadData(request);
212}
213