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