xref: /haiku/src/add-ons/kernel/file_systems/udf/Icb.cpp (revision 8b5cc9cf52bc7de09b9fa2db0f16fced0f4ae2e9)
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 = 1;
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 = 2;
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 == B_OK) {
127 		status = fData.SetTo(block);
128 		if (status == B_OK) {
129 			icb_header *header = (icb_header *)fData.Block();
130 			if (header->tag().id() == TAGID_FILE_ENTRY) {
131 				file_icb_entry *entry = (file_icb_entry *)header;
132 				PDUMP(entry);
133 				(void)entry;	// warning death
134 			} else if (header->tag().id() == TAGID_EXTENDED_FILE_ENTRY) {
135 				extended_file_icb_entry *entry
136 					= (extended_file_icb_entry *)header;
137 				PDUMP(entry);
138 				(void)entry;	// warning death
139 			} else {
140 				PDUMP(header);
141 			}
142 			status = header->tag().init_check(address.block());
143 		}
144 	}
145 
146 	if (IsFile()) {
147 		fFileCache = file_cache_create(fVolume->ID(), fId, Length());
148 		fFileMap = file_map_create(fVolume->ID(), fId, Length());
149 	}
150 
151 	fInitStatus = status;
152 	TRACE(("Icb::Icb: status = 0x%" B_PRIx32 ", `%s'\n", status,
153 		strerror(status)));
154 }
155 
156 
157 Icb::~Icb()
158 {
159 	if (fFileCache != NULL) {
160 		file_cache_delete(fFileCache);
161 		file_map_delete(fFileMap);
162 	}
163 }
164 
165 
166 status_t
167 Icb::GetDirectoryIterator(DirectoryIterator **iterator)
168 {
169 	status_t error = iterator ? B_OK : B_BAD_VALUE;
170 
171 	if (!error) {
172 		*iterator = new(std::nothrow) DirectoryIterator(this);
173 		if (*iterator)
174 		 	fIteratorList.Add(*iterator);
175 		else
176 			error = B_NO_MEMORY;
177 	}
178 
179 	return error;
180 }
181 
182 
183 status_t
184 Icb::InitCheck()
185 {
186 	return fInitStatus;
187 }
188 
189 
190 void
191 Icb::GetAccessTime(struct timespec &timespec) const
192 {
193 	timestamp ts;
194 	if (_Tag().id() == TAGID_EXTENDED_FILE_ENTRY)
195 		ts = _ExtendedEntry()->access_date_and_time();
196 	else
197 		ts = _FileEntry()->access_date_and_time();
198 
199 	if (decode_time(ts, timespec) != B_OK) {
200 		decode_time(
201 			fVolume->PrimaryVolumeDescriptor()->recording_date_and_time(),
202 			timespec);
203 	}
204 }
205 
206 
207 void
208 Icb::GetModificationTime(struct timespec &timespec) const
209 {
210 	timestamp ts;
211 	if (_Tag().id() == TAGID_EXTENDED_FILE_ENTRY)
212 		ts = _ExtendedEntry()->modification_date_and_time();
213 	else
214 		ts = _FileEntry()->modification_date_and_time();
215 
216 	if (decode_time(ts, timespec) != B_OK) {
217 		decode_time(
218 			fVolume->PrimaryVolumeDescriptor()->recording_date_and_time(),
219 			timespec);
220 	}
221 }
222 
223 
224 status_t
225 Icb::FindBlock(uint32 logicalBlock, off_t &block, bool &recorded)
226 {
227 	off_t pos = logicalBlock << fVolume->BlockShift();
228 	if (uint64(pos) >= Length()) {
229 		block = -1;
230 		return B_ERROR;
231 	}
232 
233 	DEBUG_INIT_ETC("Icb", ("pos: %" B_PRIdOFF, pos));
234 
235 	status_t status = B_OK;
236 	long_address extent;
237 	bool isEmpty = false;
238 	recorded = false;
239 
240 	switch (_IcbTag().descriptor_flags()) {
241 		case ICB_DESCRIPTOR_TYPE_SHORT:
242 		{
243 			TRACE(("Icb::FindBlock: descriptor type -> short\n"));
244 			AllocationDescriptorList<ShortDescriptorAccessor> list(this,
245 				ShortDescriptorAccessor(fPartition));
246 			status = list.FindExtent(pos, &extent, &isEmpty);
247 			if (status != B_OK) {
248 				TRACE_ERROR(("Icb::FindBlock: error finding extent for offset "
249 					"%" B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, status,
250 					strerror(status)));
251 			}
252 			break;
253 		}
254 
255 		case ICB_DESCRIPTOR_TYPE_LONG:
256 		{
257 			TRACE(("Icb::FindBlock: descriptor type -> long\n"));
258 			AllocationDescriptorList<LongDescriptorAccessor> list(this);
259 			status = list.FindExtent(pos, &extent, &isEmpty);
260 			if (status != B_OK) {
261 				TRACE_ERROR(("Icb::FindBlock: error finding extent for offset "
262 					"%" B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos,
263 					status, strerror(status)));
264 			}
265 			break;
266 		}
267 
268 		case ICB_DESCRIPTOR_TYPE_EXTENDED:
269 		{
270 			TRACE(("Icb::FindBlock: descriptor type -> extended\n"));
271 //			AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0));
272 //			RETURN(_Read(list, pos, buffer, length, block));
273 			RETURN(B_ERROR);
274 			break;
275 		}
276 
277 		case ICB_DESCRIPTOR_TYPE_EMBEDDED:
278 		{
279 			TRACE(("Icb::FindBlock: descriptor type: embedded\n"));
280 			RETURN(B_ERROR);
281 			break;
282 		}
283 
284 		default:
285 			TRACE(("Icb::FindBlock: invalid icb descriptor flags! (flags = %d)\n",
286 				_IcbTag().descriptor_flags()));
287 			RETURN(B_BAD_VALUE);
288 			break;
289 	}
290 
291 	if (status == B_OK) {
292 		block = extent.block();
293 		recorded = extent.type() == EXTENT_TYPE_RECORDED;
294 		TRACE(("Icb::FindBlock: block %" B_PRIdOFF "\n", block));
295 	}
296 	return status;
297 }
298 
299 
300 status_t
301 Icb::Read(off_t pos, void *buffer, size_t *length, uint32 *block)
302 {
303 	TRACE(("Icb::Read: pos = %" B_PRIdOFF ", buffer = %p, length = (%p)->%ld\n",
304 		pos, buffer, length, (length ? *length : 0)));
305 
306 	DEBUG_INIT_ETC("Icb", ("pos: %" B_PRIdOFF " , length: %ld", pos, *length));
307 
308 	if (fFileCache != NULL)
309 		return file_cache_read(fFileCache, NULL, pos, buffer, length);
310 
311 	if (!buffer || !length || pos < 0)
312 		return B_BAD_VALUE;
313 
314 	if (uint64(pos) >= Length()) {
315 		*length = 0;
316 		return B_OK;
317 	}
318 
319 	switch (_IcbTag().descriptor_flags()) {
320 		case ICB_DESCRIPTOR_TYPE_SHORT:
321 		{
322 			TRACE(("Icb::Read: descriptor type -> short\n"));
323 			AllocationDescriptorList<ShortDescriptorAccessor> list(this,
324 				ShortDescriptorAccessor(fPartition));
325 			RETURN(_Read(list, pos, buffer, length, block));
326 			break;
327 		}
328 
329 		case ICB_DESCRIPTOR_TYPE_LONG:
330 		{
331 			TRACE(("Icb::Read: descriptor type -> long\n"));
332 			AllocationDescriptorList<LongDescriptorAccessor> list(this);
333 			RETURN(_Read(list, pos, buffer, length, block));
334 			break;
335 		}
336 
337 		case ICB_DESCRIPTOR_TYPE_EXTENDED:
338 		{
339 			TRACE(("Icb::Read: descriptor type -> extended\n"));
340 //			AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0));
341 //			RETURN(_Read(list, pos, buffer, length, block));
342 			RETURN(B_ERROR);
343 			break;
344 		}
345 
346 		case ICB_DESCRIPTOR_TYPE_EMBEDDED:
347 		{
348 			TRACE(("Icb::Read: descriptor type: embedded\n"));
349 			RETURN(B_ERROR);
350 			break;
351 		}
352 
353 		default:
354 			TRACE(("Icb::Read: invalid icb descriptor flags! (flags = %d)\n",
355 				_IcbTag().descriptor_flags()));
356 			RETURN(B_BAD_VALUE);
357 			break;
358 	}
359 }
360 
361 
362 /*! \brief Does the dirty work of reading using the given DescriptorList object
363 	to access the allocation descriptors properly.
364 */
365 template <class DescriptorList>
366 status_t
367 Icb::_Read(DescriptorList &list, off_t pos, void *_buffer, size_t *length, uint32 *block)
368 {
369 	TRACE(("Icb::_Read(): list = %p, pos = %" B_PRIdOFF ", buffer = %p, "
370 		"length = %ld\n", &list, pos, _buffer, (length ? *length : 0)));
371 
372 	uint64 bytesLeftInFile = uint64(pos) > Length() ? 0 : Length() - pos;
373 	size_t bytesLeft = (*length >= bytesLeftInFile) ? bytesLeftInFile : *length;
374 	size_t bytesRead = 0;
375 
376 	Volume *volume = GetVolume();
377 	status_t status = B_OK;
378 	uint8 *buffer = (uint8 *)_buffer;
379 	bool isFirstBlock = true;
380 
381 	while (bytesLeft > 0) {
382 
383 		TRACE(("Icb::_Read(): pos: %" B_PRIdOFF ", bytesLeft: %ld\n", pos,
384 			bytesLeft));
385 		long_address extent;
386 		bool isEmpty = false;
387 		status = list.FindExtent(pos, &extent, &isEmpty);
388 		if (status != B_OK) {
389 			TRACE_ERROR(("Icb::_Read: error finding extent for offset %"
390 				B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, status,
391 				strerror(status)));
392 			break;
393 		}
394 
395 		TRACE(("Icb::_Read(): found extent for offset %" B_PRIdOFF ": (block: "
396 			"%" B_PRIu32 ", partition: %d, length: %" B_PRIu32 ", type: %d)\n",
397 			pos, extent.block(), extent.partition(), extent.length(),
398 			extent.type()));
399 
400 		switch (extent.type()) {
401 			case EXTENT_TYPE_RECORDED:
402 				isEmpty = false;
403 				break;
404 
405 			case EXTENT_TYPE_ALLOCATED:
406 			case EXTENT_TYPE_UNALLOCATED:
407 				isEmpty = true;
408 				break;
409 
410 			default:
411 				TRACE_ERROR(("Icb::_Read(): Invalid extent type found: %d\n",
412 					extent.type()));
413 				status = B_ERROR;
414 				break;
415 		}
416 
417 		if (status != B_OK)
418 			break;
419 
420 		// Note the unmapped first block of the total read in
421 		// the block output parameter if provided
422 		if (isFirstBlock) {
423 			isFirstBlock = false;
424 			if (block)
425 				*block = extent.block();
426 		}
427 
428 		off_t blockOffset
429 			= pos - off_t((pos >> volume->BlockShift()) << volume->BlockShift());
430 
431 		size_t readLength = volume->BlockSize() - blockOffset;
432 		if (bytesLeft < readLength)
433 			readLength = bytesLeft;
434 		if (extent.length() < readLength)
435 			readLength = extent.length();
436 
437 		TRACE(("Icb::_Read: reading block. offset = %" B_PRIdOFF
438 			", length: %ld\n", blockOffset, readLength));
439 
440 		if (isEmpty) {
441 			TRACE(("Icb::_Read: reading %ld empty bytes as zeros\n",
442 				readLength));
443 			memset(buffer, 0, readLength);
444 		} else {
445 			off_t diskBlock;
446 			status = volume->MapBlock(extent, &diskBlock);
447 			if (status != B_OK) {
448 				TRACE_ERROR(("Icb::_Read: could not map extent\n"));
449 				break;
450 			}
451 
452 			TRACE(("Icb::_Read: %ld bytes from disk block %" B_PRIdOFF " using"
453 				" block_cache_get_etc()\n", readLength, diskBlock));
454 			const uint8 *data;
455 			status = block_cache_get_etc(volume->BlockCache(),
456 				diskBlock, (const void**)&data);
457 			if (status != B_OK)
458 				break;
459 			memcpy(buffer, data + blockOffset, readLength);
460 			block_cache_put(volume->BlockCache(), diskBlock);
461 		}
462 
463 		bytesLeft -= readLength;
464 		bytesRead += readLength;
465 		pos += readLength;
466 		buffer += readLength;
467 	}
468 
469 	*length = bytesRead;
470 
471 	return status;
472 }
473 
474 
475 status_t
476 Icb::GetFileMap(off_t offset, size_t size, file_io_vec *vecs, size_t *count)
477 {
478 	switch (_IcbTag().descriptor_flags()) {
479 		case ICB_DESCRIPTOR_TYPE_SHORT:
480 		{
481 			AllocationDescriptorList<ShortDescriptorAccessor> list(this,
482 				ShortDescriptorAccessor(0));
483 			return _GetFileMap(list, offset, size, vecs, count);
484 		}
485 
486 		case ICB_DESCRIPTOR_TYPE_LONG:
487 		{
488 			AllocationDescriptorList<LongDescriptorAccessor> list(this);
489 			return _GetFileMap(list, offset, size, vecs, count);
490 		}
491 
492 		case ICB_DESCRIPTOR_TYPE_EXTENDED:
493 		case ICB_DESCRIPTOR_TYPE_EMBEDDED:
494 		default:
495 		{
496 			// TODO: implement?
497 			return B_UNSUPPORTED;
498 		}
499 	}
500 }
501 
502 
503 template<class DescriptorList>
504 status_t
505 Icb::_GetFileMap(DescriptorList &list, off_t offset, size_t size,
506 	struct file_io_vec *vecs, size_t *count)
507 {
508 	size_t index = 0;
509 	size_t max = *count;
510 
511 	while (true) {
512 		long_address extent;
513 		bool isEmpty = false;
514 		status_t status = list.FindExtent(offset, &extent, &isEmpty);
515 		if (status != B_OK)
516 			return status;
517 
518 		switch (extent.type()) {
519 			case EXTENT_TYPE_RECORDED:
520 				isEmpty = false;
521 				break;
522 
523 			case EXTENT_TYPE_ALLOCATED:
524 			case EXTENT_TYPE_UNALLOCATED:
525 				isEmpty = true;
526 				break;
527 
528 			default:
529 				return B_ERROR;
530 		}
531 
532 		if (isEmpty)
533 			vecs[index].offset = -1;
534 		else {
535 			off_t diskBlock;
536 			fVolume->MapBlock(extent, &diskBlock);
537 			vecs[index].offset = diskBlock << fVolume->BlockShift();
538 		}
539 
540 		off_t length = extent.length();
541 		vecs[index].length = length;
542 
543 		offset += length;
544 		size -= length;
545 		index++;
546 
547 		if (index >= max || (off_t)size <= vecs[index - 1].length
548 			|| offset >= (off_t)Length()) {
549 			*count = index;
550 			return index >= max ? B_BUFFER_OVERFLOW : B_OK;
551 		}
552 	}
553 
554 	// can never get here
555 	return B_ERROR;
556 }
557 
558 
559 status_t
560 Icb::Find(const char *filename, ino_t *id)
561 {
562 	TRACE(("Icb::Find: filename = `%s', id = %p\n", filename, id));
563 
564 	if (!filename || !id)
565 		return B_BAD_VALUE;
566 
567 	DirectoryIterator *i;
568 	status_t status = GetDirectoryIterator(&i);
569 	if (status != B_OK)
570 		return status;
571 
572 	ino_t entryId;
573 	uint32 length = B_FILE_NAME_LENGTH;
574 	char name[B_FILE_NAME_LENGTH];
575 
576 	bool foundIt = false;
577 	while (i->GetNextEntry(name, &length, &entryId) == B_OK) {
578 		if (strcmp(filename, name) == 0) {
579 			foundIt = true;
580 			break;
581 		}
582 
583 		// reset overwritten length
584 		length = B_FILE_NAME_LENGTH;
585 	}
586 
587 	if (foundIt)
588 		*id = entryId;
589 	else
590 		status = B_ENTRY_NOT_FOUND;
591 
592 	return status;
593 }
594