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