1//----------------------------------------------------------------------
2//  This software is part of the OpenBeOS distribution and is covered
3//  by the OpenBeOS license.
4//
5//  Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net
6//---------------------------------------------------------------------
7#ifndef _UDF_ICB_H
8#define _UDF_ICB_H
9
10/*! \file Icb.h
11*/
12
13#ifndef _IMPEXP_KERNEL
14#	define _IMPEXP_KERNEL
15#endif
16#ifdef COMPILE_FOR_R5
17extern "C" {
18#endif
19	#include "fsproto.h"
20#ifdef COMPILE_FOR_R5
21}
22#endif
23
24#include "kernel_cpp.h"
25#include "UdfDebug.h"
26
27#include "CachedBlock.h"
28#include "UdfStructures.h"
29#include "SinglyLinkedList.h"
30
31namespace Udf {
32
33class DirectoryIterator;
34class Volume;
35
36/*! \brief Abstract interface to file entry structure members
37	that are not commonly accessible through file_icb_entry().
38
39	This is necessary, since we can't use virtual functions in
40	the disk structure structs themselves, since we generally
41	don't create disk structure objects by calling new, but
42	rather just cast a chunk of memory read off disk to be
43	a pointer to the struct of interest (which works fine
44	for regular functions, but fails miserably for virtuals
45	due to the vtable not being setup properly).
46*/
47class AbstractFileEntry {
48public:
49	virtual uint8* AllocationDescriptors() = 0;
50	virtual uint32 AllocationDescriptorsLength() = 0;
51};
52
53template <class Descriptor>
54class FileEntry : public AbstractFileEntry {
55public:
56	FileEntry(CachedBlock *descriptorBlock = NULL);
57	void SetTo(CachedBlock *descriptorBlock);
58	virtual uint8* AllocationDescriptors();
59	virtual uint32 AllocationDescriptorsLength();
60private:
61	Descriptor* _Descriptor();
62
63	CachedBlock *fDescriptorBlock;
64};
65
66class Icb {
67public:
68	Icb(Volume *volume, long_address address);
69	status_t InitCheck();
70	vnode_id Id() { return fId; }
71
72	// categorization
73	uint8 Type() { return IcbTag().file_type(); }
74	bool IsFile() { return InitCheck() == B_OK && Type() == ICB_TYPE_REGULAR_FILE; }
75	bool IsDirectory() { return InitCheck() == B_OK
76	                     && (Type() == ICB_TYPE_DIRECTORY || Type() == ICB_TYPE_STREAM_DIRECTORY); }
77
78	uint32 Uid() { return 0; }//FileEntry()->uid(); }
79	uint32 Gid() { return 0; }
80	uint16 FileLinkCount() { return FileEntry()->file_link_count(); }
81	uint64 Length() { return FileEntry()->information_length(); }
82	mode_t Mode() { return (IsDirectory() ? S_IFDIR : S_IFREG) | S_IRUSR | S_IRGRP | S_IROTH; }
83	time_t AccessTime();
84	time_t ModificationTime();
85
86	uint8 *AllocationDescriptors() { return AbstractEntry()->AllocationDescriptors(); }
87	uint32 AllocationDescriptorsSize() { return AbstractEntry()->AllocationDescriptorsLength(); }
88
89	status_t Read(off_t pos, void *buffer, size_t *length, uint32 *block = NULL);
90
91	// for directories only
92	status_t GetDirectoryIterator(DirectoryIterator **iterator);
93	status_t Find(const char *filename, vnode_id *id);
94
95	Volume* GetVolume() const { return fVolume; }
96
97private:
98	Icb();	// unimplemented
99
100	descriptor_tag & Tag() { return (reinterpret_cast<icb_header*>(fData.Block()))->tag(); }
101	icb_entry_tag& IcbTag() { return (reinterpret_cast<icb_header*>(fData.Block()))->icb_tag(); }
102	AbstractFileEntry* AbstractEntry() {
103		DEBUG_INIT("Icb");
104		return (Tag().id() == TAGID_EXTENDED_FILE_ENTRY)
105//	             ? reinterpret_cast<extended_file_icb_entry*>(fData.Block())
106//	             : reinterpret_cast<file_icb_entry*>(fData.Block()));
107	             ? &fExtendedEntry
108	             : &fFileEntry;
109	}
110	file_icb_entry* FileEntry() { return (reinterpret_cast<file_icb_entry*>(fData.Block())); }
111	extended_file_icb_entry& ExtendedEntry() { return *(reinterpret_cast<extended_file_icb_entry*>(fData.Block())); }
112
113	template <class DescriptorList>
114	status_t _Read(DescriptorList &list, off_t pos, void *buffer, size_t *length, uint32 *block);
115
116
117private:
118	Volume *fVolume;
119	CachedBlock fData;
120	status_t fInitStatus;
121	vnode_id fId;
122	SinglyLinkedList<DirectoryIterator*> fIteratorList;
123	/* [zooey]: gcc-2.95.3 requires the explicit namespace here, otherwise
124	   it complains about a syntax error(!). This is most probably a bug. */
125	Udf::FileEntry<file_icb_entry> fFileEntry;
126	Udf::FileEntry<extended_file_icb_entry> fExtendedEntry;
127};
128
129/*! \brief Does the dirty work of reading using the given DescriptorList object
130	to access the allocation descriptors properly.
131*/
132template <class DescriptorList>
133status_t
134Icb::_Read(DescriptorList &list, off_t pos, void *_buffer, size_t *length, uint32 *block)
135{
136	DEBUG_INIT_ETC("Icb", ("list: %p, pos: %Ld, buffer: %p, length: (%p)->%ld",
137	               &list, pos, _buffer, length, (length ? *length : 0)));
138	if (!_buffer || !length)
139		RETURN(B_BAD_VALUE);
140
141	uint64 bytesLeftInFile = uint64(pos) > Length() ? 0 : Length() - pos;
142	size_t bytesLeft = (*length >= bytesLeftInFile) ? bytesLeftInFile : *length;
143	size_t bytesRead = 0;
144
145	Volume *volume = GetVolume();
146	status_t error = B_OK;
147	uint8 *buffer = reinterpret_cast<uint8*>(_buffer);
148	bool isFirstBlock = true;
149
150	while (bytesLeft > 0 && !error) {
151
152		PRINT(("pos: %Ld\n", pos));
153		PRINT(("bytesLeft: %ld\n", bytesLeft));
154
155		long_address extent;
156		bool isEmpty = false;
157		error = list.FindExtent(pos, &extent, &isEmpty);
158		if (!error) {
159			PRINT(("found extent for offset %Ld: (block: %ld, partition: %d, length: %ld, type: %d)\n",
160			       pos, extent.block(), extent.partition(), extent.length(), extent.type()));
161
162			switch (extent.type()) {
163				case EXTENT_TYPE_RECORDED:
164					isEmpty = false;
165					break;
166
167				case EXTENT_TYPE_ALLOCATED:
168				case EXTENT_TYPE_UNALLOCATED:
169					isEmpty = true;
170					break;
171
172				default:
173					PRINT(("Invalid extent type found: %d\n", extent.type()));
174					error = B_ERROR;
175					break;
176			}
177
178			if (!error) {
179				// Note the unmapped first block of the total read in
180				// the block output parameter if provided
181				if (isFirstBlock) {
182					isFirstBlock = false;
183					if (block)
184						*block = extent.block();
185				}
186
187				off_t blockOffset = pos - off_t((pos >> volume->BlockShift()) << volume->BlockShift());
188				size_t fullBlocksLeft = bytesLeft >> volume->BlockShift();
189
190				if (fullBlocksLeft > 0 && blockOffset == 0) {
191					PRINT(("reading full block (or more)\n"));
192					// Block aligned and at least one full block left. Read in using
193					// cached_read() calls.
194					off_t diskBlock;
195					error = volume->MapBlock(extent, &diskBlock);
196					if (!error) {
197						size_t fullBlockBytesLeft = fullBlocksLeft << volume->BlockShift();
198						size_t readLength = fullBlockBytesLeft < extent.length()
199						                      ? fullBlockBytesLeft
200						                      : extent.length();
201
202						if (isEmpty) {
203							PRINT(("reading %ld empty bytes as zeros\n", readLength));
204							memset(buffer, 0, readLength);
205						} else {
206							off_t diskBlock;
207							error = volume->MapBlock(extent, &diskBlock);
208							if (!error) {
209								PRINT(("reading %ld bytes from disk block %Ld using cached_read()\n",
210								       readLength, diskBlock));
211								error = cached_read(volume->Device(), diskBlock, buffer,
212								                  readLength >> volume->BlockShift(),
213								                  volume->BlockSize());
214							}
215						}
216
217						if (!error) {
218							bytesLeft -= readLength;
219							bytesRead += readLength;
220							pos += readLength;
221							buffer += readLength;
222						}
223					}
224
225				} else {
226					PRINT(("partial block\n"));
227					off_t partialOffset;
228					size_t partialLength;
229					if (blockOffset == 0) {
230						// Block aligned, but only a partial block's worth remaining. Read
231						// in remaining bytes of file
232						partialOffset = 0;
233						partialLength = bytesLeft;
234					} else {
235						// Not block aligned, so just read up to the next block boundary.
236						partialOffset = blockOffset;
237						partialLength = volume->BlockSize() - blockOffset;
238						if (bytesLeft < partialLength)
239							partialLength = bytesLeft;
240					}
241
242					PRINT(("partialOffset: %Ld\n", partialOffset));
243					PRINT(("partialLength: %ld\n", partialLength));
244
245					if (isEmpty) {
246						PRINT(("reading %ld empty bytes as zeros\n", partialLength));
247						memset(buffer, 0, partialLength);
248					} else {
249						off_t diskBlock;
250						error = volume->MapBlock(extent, &diskBlock);
251						if (!error) {
252							PRINT(("reading %ld bytes from disk block %Ld using get_block()\n",
253							       partialLength, diskBlock));
254							uint8 *data = (uint8*)get_block(volume->Device(), diskBlock, volume->BlockSize());
255							error = data ? B_OK : B_BAD_DATA;
256							if (!error) {
257								memcpy(buffer, data+partialOffset, partialLength);
258								release_block(volume->Device(), diskBlock);
259							}
260						}
261					}
262
263					if (!error) {
264						bytesLeft -= partialLength;
265						bytesRead += partialLength;
266						pos += partialLength;
267						buffer += partialLength;
268					}
269				}
270			}
271		} else {
272			PRINT(("error finding extent for offset %Ld: 0x%lx, `%s'", pos,
273			       error, strerror(error)));
274			break;
275		}
276	}
277
278	*length = bytesRead;
279
280	RETURN(error);
281}
282
283template <class Descriptor>
284FileEntry<Descriptor>::FileEntry(CachedBlock *descriptorBlock)
285	: fDescriptorBlock(descriptorBlock)
286{
287}
288
289template <class Descriptor>
290void
291FileEntry<Descriptor>::SetTo(CachedBlock *descriptorBlock)
292{
293	fDescriptorBlock = descriptorBlock;
294}
295
296template <class Descriptor>
297uint8*
298FileEntry<Descriptor>::AllocationDescriptors()
299{
300	Descriptor* descriptor = _Descriptor();
301	return descriptor ? descriptor->allocation_descriptors() : NULL;
302}
303
304template <class Descriptor>
305uint32
306FileEntry<Descriptor>::AllocationDescriptorsLength()
307{
308	Descriptor* descriptor = _Descriptor();
309	return descriptor ? descriptor->allocation_descriptors_length() : 0;
310}
311
312template <class Descriptor>
313Descriptor*
314FileEntry<Descriptor>::_Descriptor()
315{
316	return fDescriptorBlock ? reinterpret_cast<Descriptor*>(fDescriptorBlock->Block()) : NULL;
317}
318
319};	// namespace Udf
320
321#endif	// _UDF_ICB_H
322