1/*
2 * Copyright 2009-2014, 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
14#include <DataIO.h>
15#include <package/hpkg/DataReader.h>
16#include <package/hpkg/PackageDataReader.h>
17
18#include <AutoDeleter.h>
19#include <util/AutoLock.h>
20
21#include "DebugSupport.h"
22#include "ClassCache.h"
23#include "Package.h"
24
25
26using namespace BPackageKit::BHPKG;
27
28
29// #pragma mark - class cache
30
31
32CLASS_CACHE(PackageFile);
33
34
35// #pragma mark - DataAccessor
36
37
38struct PackageFile::IORequestOutput : BDataIO {
39public:
40	IORequestOutput(io_request* request)
41		:
42		fRequest(request)
43	{
44	}
45
46	virtual ssize_t Write(const void* buffer, size_t size)
47	{
48		status_t error = write_to_io_request(fRequest, buffer, size);
49		RETURN_ERROR(error == B_OK ? (ssize_t)size : (ssize_t)error);
50	}
51
52private:
53	io_request*	fRequest;
54};
55
56
57struct PackageFile::DataAccessor {
58	DataAccessor(Package* package, PackageData* data)
59		:
60		fPackage(package),
61		fData(data),
62		fReader(NULL),
63		fFileCache(NULL)
64	{
65		mutex_init(&fLock, "file data accessor");
66	}
67
68	~DataAccessor()
69	{
70		file_cache_delete(fFileCache);
71		delete fReader;
72		mutex_destroy(&fLock);
73	}
74
75	status_t Init(dev_t deviceID, ino_t nodeID, int fd)
76	{
77		// create a reader for the data
78		status_t error = fPackage->CreateDataReader(*fData, fReader);
79		if (error != B_OK)
80			return error;
81
82		// create a file cache
83		fFileCache = file_cache_create(deviceID, nodeID,
84			fData->UncompressedSize());
85		if (fFileCache == NULL)
86			RETURN_ERROR(B_NO_MEMORY);
87
88		return B_OK;
89	}
90
91	status_t ReadData(off_t offset, void* buffer, size_t* bufferSize)
92	{
93		return file_cache_read(fFileCache, NULL, offset, buffer, bufferSize);
94	}
95
96	status_t ReadData(io_request* request)
97	{
98		off_t offset = io_request_offset(request);
99		size_t size = io_request_length(request);
100
101		if (offset < 0 || (uint64)offset > fData->UncompressedSize())
102			RETURN_ERROR(B_BAD_VALUE);
103
104		size_t toRead = std::min((uint64)size,
105			fData->UncompressedSize() - offset);
106
107		if (toRead > 0) {
108			IORequestOutput output(request);
109			MutexLocker locker(fLock, false, fData->Version() == 1);
110				// V2 readers are reentrant
111			status_t error = fReader->ReadDataToOutput(offset, toRead, &output);
112			if (error != B_OK)
113				RETURN_ERROR(error);
114		}
115
116		return B_OK;
117	}
118
119private:
120	mutex							fLock;
121	Package*						fPackage;
122	PackageData*					fData;
123	BAbstractBufferedDataReader*	fReader;
124	void*							fFileCache;
125};
126
127
128// #pragma mark - PackageFile
129
130
131PackageFile::PackageFile(Package* package, mode_t mode, const PackageData& data)
132	:
133	PackageLeafNode(package, mode),
134	fData(data),
135	fDataAccessor(NULL)
136{
137}
138
139
140PackageFile::~PackageFile()
141{
142}
143
144
145status_t
146PackageFile::VFSInit(dev_t deviceID, ino_t nodeID)
147{
148	status_t error = PackageNode::VFSInit(deviceID, nodeID);
149	if (error != B_OK)
150		return error;
151	MethodDeleter<PackageNode, void, &PackageNode::NonVirtualVFSUninit>
152		baseClassUninit(this);
153
154	// open the package -- that's already done by PackageNode::VFSInit(), so it
155	// shouldn't fail here. We only need to do it again, since we need the FD.
156	BReference<Package> package(GetPackage());
157	int fd = package->Open();
158	if (fd < 0)
159		RETURN_ERROR(fd);
160	PackageCloser packageCloser(package);
161
162	// create the data accessor
163	fDataAccessor = new(std::nothrow) DataAccessor(package, &fData);
164	if (fDataAccessor == NULL)
165		RETURN_ERROR(B_NO_MEMORY);
166
167	error = fDataAccessor->Init(deviceID, nodeID, fd);
168	if (error != B_OK) {
169		delete fDataAccessor;
170		fDataAccessor = NULL;
171		return error;
172	}
173
174	baseClassUninit.Detach();
175	return B_OK;
176}
177
178
179void
180PackageFile::VFSUninit()
181{
182	if (fDataAccessor != NULL) {
183		delete fDataAccessor;
184		fDataAccessor = NULL;
185	}
186
187	PackageNode::VFSUninit();
188}
189
190
191off_t
192PackageFile::FileSize() const
193{
194	return fData.UncompressedSize();
195}
196
197
198status_t
199PackageFile::Read(off_t offset, void* buffer, size_t* bufferSize)
200{
201	if (fDataAccessor == NULL)
202		return B_BAD_VALUE;
203	return fDataAccessor->ReadData(offset, buffer, bufferSize);
204}
205
206
207status_t
208PackageFile::Read(io_request* request)
209{
210	if (fDataAccessor == NULL)
211		return B_BAD_VALUE;
212	return fDataAccessor->ReadData(request);
213}
214