xref: /haiku/src/add-ons/kernel/file_systems/udf/Icb.cpp (revision d374a27286b8a52974a97dba0d5966ea026a665d)
1 /*
2  * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
3  * Copyright 2010, Michael Lotz, mmlr@mlotz.ch.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 #include "Icb.h"
8 
9 #include "time.h"
10 
11 #include "AllocationDescriptorList.h"
12 #include "Utils.h"
13 #include "Volume.h"
14 
15 #include <file_cache.h>
16 
17 
18 status_t
19 DirectoryIterator::GetNextEntry(char *name, uint32 *length, ino_t *id)
20 {
21 	if (!id || !name || !length)
22 		return B_BAD_VALUE;
23 
24 	TRACE(("DirectoryIterator::GetNextEntry: name = %p, length = %ld, "
25 		"id = %p, position = %Ld, parent length = %Ld\n", name, *length, id,
26 		fPosition, Parent()->Length()));
27 
28 	status_t status = B_OK;
29 	if (fAtBeginning) {
30 		TRACE(("DirectoryIterator::GetNextEntry: .\n"));
31 		sprintf(name, ".");
32 		*length = 2;
33 		*id = Parent()->Id();
34 		fAtBeginning = false;
35 	} else {
36 		if (uint64(fPosition) >= Parent()->Length()) {
37 			TRACE(("DirectoryIterator::GetNextEntry: end of dir\n"));
38 			return B_ENTRY_NOT_FOUND;
39 		}
40 
41 		uint8 data[kMaxFileIdSize];
42 		file_id_descriptor *entry = (file_id_descriptor *)data;
43 
44 		uint32 block = 0;
45 		off_t offset = fPosition;
46 
47 		size_t entryLength = kMaxFileIdSize;
48 		// First read in the static portion of the file id descriptor,
49 		// then, based on the information therein, read in the variable
50 		// length tail portion as well.
51 		status = Parent()->Read(offset, entry, &entryLength, &block);
52 		if (!status && entryLength >= sizeof(file_id_descriptor)
53 			&& entry->tag().init_check(block) == B_OK) {
54 			PDUMP(entry);
55 			offset += entry->total_length();
56 
57 			if (entry->is_parent()) {
58 				TRACE(("DirectoryIterator::GetNextEntry: ..\n"));
59 				sprintf(name, "..");
60 				*length = 3;
61 			} else {
62 				UdfString string(entry->id(), entry->id_length());
63 				TRACE(("DirectoryIterator::GetNextEntry: UfdString id == `%s', "
64 				"length = %lu\n", string.Utf8(), string.Utf8Length()));
65 				DUMP(entry->icb());
66 				sprintf(name, "%s", string.Utf8());
67 				*length = string.Utf8Length();
68 			}
69 			*id = to_vnode_id(entry->icb());
70 		}
71 
72 		if (!status)
73 			fPosition = offset;
74 	}
75 
76  	return status;
77 }
78 
79 
80 /*	\brief Rewinds the iterator to point to the first entry in the directory. */
81 void
82 DirectoryIterator::Rewind()
83 {
84 	fAtBeginning = true;
85 	fPosition = 0;
86 }
87 
88 
89 //	#pragma mark - Private methods
90 
91 
92 DirectoryIterator::DirectoryIterator(Icb *parent)
93 	:
94 	fAtBeginning(true),
95 	fParent(parent),
96 	fPosition(0)
97 {
98 }
99 
100 
101 Icb::Icb(Volume *volume, long_address address)
102 	:
103 	fVolume(volume),
104 	fData(volume),
105 	fInitStatus(B_NO_INIT),
106 	fId(to_vnode_id(address)),
107 	fFileEntry(&fData),
108 	fExtendedEntry(&fData),
109 	fFileCache(NULL),
110 	fFileMap(NULL)
111 {
112 	TRACE(("Icb::Icb: volume = %p, address(block = %ld, partition = %d, "
113 		"length = %ld)\n", volume, address.block(), address.partition(),
114 		address.length()));
115 
116 	if (volume == NULL)
117 		fInitStatus = B_BAD_VALUE;
118 
119 	off_t block;
120 	status_t status = fVolume->MapBlock(address, &block);
121 	if (!status) {
122 		icb_header *header = (icb_header *)fData.SetTo(block);
123 		if (header->tag().id() == TAGID_FILE_ENTRY) {
124 			file_icb_entry *entry = (file_icb_entry *)header;
125 			PDUMP(entry);
126 			(void)entry;	// warning death
127 		} else if (header->tag().id() == TAGID_EXTENDED_FILE_ENTRY) {
128 			extended_file_icb_entry *entry = (extended_file_icb_entry *)header;
129 			PDUMP(entry);
130 			(void)entry;	// warning death
131 		} else {
132 			PDUMP(header);
133 		}
134 		status = header->tag().init_check(address.block());
135 	}
136 
137 	if (IsFile()) {
138 		fFileCache = file_cache_create(fVolume->ID(), fId, Length());
139 		fFileMap = file_map_create(fVolume->ID(), fId, Length());
140 	}
141 
142 	fInitStatus = status;
143 	TRACE(("Icb::Icb: status = 0x%lx, `%s'\n", status, strerror(status)));
144 }
145 
146 
147 Icb::~Icb()
148 {
149 	if (fFileCache != NULL) {
150 		file_cache_delete(fFileCache);
151 		file_map_delete(fFileMap);
152 	}
153 }
154 
155 
156 status_t
157 Icb::GetDirectoryIterator(DirectoryIterator **iterator)
158 {
159 	status_t error = iterator ? B_OK : B_BAD_VALUE;
160 
161 	if (!error) {
162 		*iterator = new(std::nothrow) DirectoryIterator(this);
163 		if (*iterator)
164 		 	fIteratorList.Add(*iterator);
165 		else
166 			error = B_NO_MEMORY;
167 	}
168 
169 	return error;
170 }
171 
172 
173 status_t
174 Icb::InitCheck()
175 {
176 	return fInitStatus;
177 }
178 
179 
180 time_t
181 Icb::AccessTime()
182 {
183 	return make_time(_FileEntry()->access_date_and_time());
184 }
185 
186 
187 time_t
188 Icb::ModificationTime()
189 {
190 	return make_time(_FileEntry()->modification_date_and_time());
191 }
192 
193 
194 status_t
195 Icb::Read(off_t pos, void *buffer, size_t *length, uint32 *block)
196 {
197 	TRACE(("Icb::Read: pos = %Ld, buffer = %p, length = (%p)->%ld\n",
198 		pos, buffer, length, (length ? *length : 0)));
199 
200 	if (!buffer || !length || pos < 0)
201 		return B_BAD_VALUE;
202 
203 	if (uint64(pos) >= Length()) {
204 		*length = 0;
205 		return B_OK;
206 	}
207 
208 	if (fFileCache != NULL)
209 		return file_cache_read(fFileCache, NULL, pos, buffer, length);
210 
211 	switch (_IcbTag().descriptor_flags()) {
212 		case ICB_DESCRIPTOR_TYPE_SHORT: {
213 			TRACE(("Icb::Read: descriptor type -> short\n"));
214 			AllocationDescriptorList<ShortDescriptorAccessor> list(this, ShortDescriptorAccessor(0));
215 			RETURN(_Read(list, pos, buffer, length, block));
216 			break;
217 		}
218 
219 		case ICB_DESCRIPTOR_TYPE_LONG: {
220 			TRACE(("Icb::Read: descriptor type -> long\n"));
221 			AllocationDescriptorList<LongDescriptorAccessor> list(this);
222 			RETURN(_Read(list, pos, buffer, length, block));
223 			break;
224 		}
225 
226 		case ICB_DESCRIPTOR_TYPE_EXTENDED: {
227 			TRACE(("Icb::Read: descriptor type -> extended\n"));
228 //			AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0));
229 //			RETURN(_Read(list, pos, buffer, length, block));
230 			RETURN(B_ERROR);
231 			break;
232 		}
233 
234 		case ICB_DESCRIPTOR_TYPE_EMBEDDED: {
235 			TRACE(("Icb::Read: descriptor type: embedded\n"));
236 			RETURN(B_ERROR);
237 			break;
238 		}
239 
240 		default:
241 			TRACE(("Icb::Read: invalid icb descriptor flags! (flags = %d)\n",
242 				_IcbTag().descriptor_flags()));
243 			RETURN(B_BAD_VALUE);
244 			break;
245 	}
246 }
247 
248 
249 /*! \brief Does the dirty work of reading using the given DescriptorList object
250 	to access the allocation descriptors properly.
251 */
252 template <class DescriptorList>
253 status_t
254 Icb::_Read(DescriptorList &list, off_t pos, void *_buffer, size_t *length, uint32 *block)
255 {
256 	TRACE(("Icb::_Read(): list = %p, pos = %Ld, buffer = %p, length = %ld\n",
257 		&list, pos, _buffer, (length ? *length : 0)));
258 
259 	uint64 bytesLeftInFile = uint64(pos) > Length() ? 0 : Length() - pos;
260 	size_t bytesLeft = (*length >= bytesLeftInFile) ? bytesLeftInFile : *length;
261 	size_t bytesRead = 0;
262 
263 	Volume *volume = GetVolume();
264 	status_t status = B_OK;
265 	uint8 *buffer = (uint8 *)_buffer;
266 	bool isFirstBlock = true;
267 
268 	while (bytesLeft > 0) {
269 
270 		TRACE(("Icb::_Read(): pos: %Ld, bytesLeft: %ld\n", pos, bytesLeft));
271 		long_address extent;
272 		bool isEmpty = false;
273 		status = list.FindExtent(pos, &extent, &isEmpty);
274 		if (status != B_OK) {
275 			TRACE_ERROR(("Icb::_Read: error finding extent for offset %Ld. "
276 				"status = 0x%lx `%s'\n", pos, status, strerror(status)));
277 			break;
278 		}
279 
280 		TRACE(("Icb::_Read(): found extent for offset %Ld: (block: %ld, "
281 			"partition: %d, length: %ld, type: %d)\n", pos, extent.block(),
282 			extent.partition(), extent.length(), extent.type()));
283 
284 		switch (extent.type()) {
285 			case EXTENT_TYPE_RECORDED:
286 				isEmpty = false;
287 				break;
288 
289 			case EXTENT_TYPE_ALLOCATED:
290 			case EXTENT_TYPE_UNALLOCATED:
291 				isEmpty = true;
292 				break;
293 
294 			default:
295 				TRACE_ERROR(("Icb::_Read(): Invalid extent type found: %d\n",
296 					extent.type()));
297 				status = B_ERROR;
298 				break;
299 		}
300 
301 		if (status != B_OK)
302 			break;
303 
304 		// Note the unmapped first block of the total read in
305 		// the block output parameter if provided
306 		if (isFirstBlock) {
307 			isFirstBlock = false;
308 			if (block)
309 				*block = extent.block();
310 		}
311 
312 		off_t blockOffset
313 			= pos - off_t((pos >> volume->BlockShift()) << volume->BlockShift());
314 
315 		size_t readLength = volume->BlockSize() - blockOffset;
316 		if (bytesLeft < readLength)
317 			readLength = bytesLeft;
318 		if (extent.length() < readLength)
319 			readLength = extent.length();
320 
321 		TRACE(("Icb::_Read: reading block. offset = %Ld, length: %ld\n",
322 			blockOffset, readLength));
323 
324 		if (isEmpty) {
325 			TRACE(("Icb::_Read: reading %ld empty bytes as zeros\n",
326 				readLength));
327 			memset(buffer, 0, readLength);
328 		} else {
329 			off_t diskBlock;
330 			status = volume->MapBlock(extent, &diskBlock);
331 			if (status != B_OK) {
332 				TRACE_ERROR(("Icb::_Read: could not map extent\n"));
333 				break;
334 			}
335 
336 			TRACE(("Icb::_Read: %ld bytes from disk block %Ld using "
337 				"block_cache_get_etc()\n", readLength, diskBlock));
338 			uint8 *data = (uint8*)block_cache_get_etc(volume->BlockCache(),
339 				diskBlock, 0, readLength);
340 			if (data == NULL)
341 				break;
342 			memcpy(buffer, data + blockOffset, readLength);
343 			block_cache_put(volume->BlockCache(), diskBlock);
344 		}
345 
346 		bytesLeft -= readLength;
347 		bytesRead += readLength;
348 		pos += readLength;
349 		buffer += readLength;
350 	}
351 
352 	*length = bytesRead;
353 
354 	return status;
355 }
356 
357 
358 status_t
359 Icb::GetFileMap(off_t offset, size_t size, file_io_vec *vecs, size_t *count)
360 {
361 	switch (_IcbTag().descriptor_flags()) {
362 		case ICB_DESCRIPTOR_TYPE_SHORT:
363 		{
364 			AllocationDescriptorList<ShortDescriptorAccessor> list(this,
365 				ShortDescriptorAccessor(0));
366 			return _GetFileMap(list, offset, size, vecs, count);
367 		}
368 
369 		case ICB_DESCRIPTOR_TYPE_LONG:
370 		{
371 			AllocationDescriptorList<LongDescriptorAccessor> list(this);
372 			return _GetFileMap(list, offset, size, vecs, count);
373 		}
374 
375 		case ICB_DESCRIPTOR_TYPE_EXTENDED:
376 		case ICB_DESCRIPTOR_TYPE_EMBEDDED:
377 		default:
378 		{
379 			// TODO: implement?
380 			return B_UNSUPPORTED;
381 		}
382 	}
383 }
384 
385 
386 template<class DescriptorList>
387 status_t
388 Icb::_GetFileMap(DescriptorList &list, off_t offset, size_t size,
389 	struct file_io_vec *vecs, size_t *count)
390 {
391 	size_t index = 0;
392 	size_t max = *count;
393 
394 	while (true) {
395 		long_address extent;
396 		bool isEmpty = false;
397 		status_t status = list.FindExtent(offset, &extent, &isEmpty);
398 		if (status != B_OK)
399 			return status;
400 
401 		switch (extent.type()) {
402 			case EXTENT_TYPE_RECORDED:
403 				isEmpty = false;
404 				break;
405 
406 			case EXTENT_TYPE_ALLOCATED:
407 			case EXTENT_TYPE_UNALLOCATED:
408 				isEmpty = true;
409 				break;
410 
411 			default:
412 				return B_ERROR;
413 		}
414 
415 		if (isEmpty)
416 			vecs[index].offset = -1;
417 		else {
418 			off_t diskBlock;
419 			fVolume->MapBlock(extent, &diskBlock);
420 			vecs[index].offset = diskBlock << fVolume->BlockShift();
421 		}
422 
423 		off_t length = extent.length();
424 		vecs[index].length = length;
425 
426 		offset += length;
427 		size -= length;
428 		index++;
429 
430 		if (index >= max || size <= vecs[index - 1].length
431 			|| offset >= (off_t)Length()) {
432 			*count = index;
433 			return index >= max ? B_BUFFER_OVERFLOW : B_OK;
434 		}
435 	}
436 
437 	// can never get here
438 	return B_ERROR;
439 }
440 
441 
442 status_t
443 Icb::Find(const char *filename, ino_t *id)
444 {
445 	TRACE(("Icb::Find: filename = `%s', id = %p\n", filename, id));
446 
447 	if (!filename || !id)
448 		RETURN(B_BAD_VALUE);
449 
450 	DirectoryIterator *i;
451 	status_t status = GetDirectoryIterator(&i);
452 	if (status != B_OK)
453 		return status;
454 
455 	ino_t entryId;
456 	uint32 length = B_FILE_NAME_LENGTH;
457 	char name[B_FILE_NAME_LENGTH];
458 
459 	bool foundIt = false;
460 	while (i->GetNextEntry(name, &length, &entryId) == B_OK) {
461 		if (strcmp(filename, name) == 0) {
462 			foundIt = true;
463 			break;
464 		}
465 
466 		// reset overwritten length
467 		length = B_FILE_NAME_LENGTH;
468 	}
469 
470 	if (foundIt)
471 		*id = entryId;
472 	else
473 		status = B_ENTRY_NOT_FOUND;
474 
475 	return status;
476 }
477