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