xref: /haiku/src/add-ons/kernel/file_systems/udf/Icb.h (revision 3e216965baa8d58a67bf7372e2bfa13d999f5a9d)
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
17 extern "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 
31 namespace Udf {
32 
33 class DirectoryIterator;
34 class 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 */
47 class AbstractFileEntry {
48 public:
49 	virtual uint8* AllocationDescriptors() = 0;
50 	virtual uint32 AllocationDescriptorsLength() = 0;
51 };
52 
53 template <class Descriptor>
54 class FileEntry : public AbstractFileEntry {
55 public:
56 	FileEntry(CachedBlock *descriptorBlock = NULL);
57 	void SetTo(CachedBlock *descriptorBlock);
58 	virtual uint8* AllocationDescriptors();
59 	virtual uint32 AllocationDescriptorsLength();
60 private:
61 	Descriptor* _Descriptor();
62 
63 	CachedBlock *fDescriptorBlock;
64 };
65 
66 class Icb {
67 public:
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 
97 private:
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 
117 private:
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 */
132 template <class DescriptorList>
133 status_t
134 Icb::_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 
283 template <class Descriptor>
284 FileEntry<Descriptor>::FileEntry(CachedBlock *descriptorBlock)
285 	: fDescriptorBlock(descriptorBlock)
286 {
287 }
288 
289 template <class Descriptor>
290 void
291 FileEntry<Descriptor>::SetTo(CachedBlock *descriptorBlock)
292 {
293 	fDescriptorBlock = descriptorBlock;
294 }
295 
296 template <class Descriptor>
297 uint8*
298 FileEntry<Descriptor>::AllocationDescriptors()
299 {
300 	Descriptor* descriptor = _Descriptor();
301 	return descriptor ? descriptor->allocation_descriptors() : NULL;
302 }
303 
304 template <class Descriptor>
305 uint32
306 FileEntry<Descriptor>::AllocationDescriptorsLength()
307 {
308 	Descriptor* descriptor = _Descriptor();
309 	return descriptor ? descriptor->allocation_descriptors_length() : 0;
310 }
311 
312 template <class Descriptor>
313 Descriptor*
314 FileEntry<Descriptor>::_Descriptor()
315 {
316 	return fDescriptorBlock ? reinterpret_cast<Descriptor*>(fDescriptorBlock->Block()) : NULL;
317 }
318 
319 };	// namespace Udf
320 
321 #endif	// _UDF_ICB_H
322