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
19status_t
20DirectoryIterator::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. */
83void
84DirectoryIterator::Rewind()
85{
86	fAtBeginning = true;
87	fPosition = 0;
88}
89
90
91//	#pragma mark - Private methods
92
93
94DirectoryIterator::DirectoryIterator(Icb *parent)
95	:
96	fAtBeginning(true),
97	fParent(parent),
98	fPosition(0)
99{
100}
101
102
103Icb::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
157Icb::~Icb()
158{
159	if (fFileCache != NULL) {
160		file_cache_delete(fFileCache);
161		file_map_delete(fFileMap);
162	}
163}
164
165
166status_t
167Icb::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
183status_t
184Icb::InitCheck()
185{
186	return fInitStatus;
187}
188
189
190void
191Icb::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
207void
208Icb::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
224status_t
225Icb::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
300status_t
301Icb::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*/
365template <class DescriptorList>
366status_t
367Icb::_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, 0, readLength, (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
475status_t
476Icb::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
503template<class DescriptorList>
504status_t
505Icb::_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
559status_t
560Icb::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