1/*
2 * Copyright 2017, Ch��� V�� Gia Hy, cvghy116@gmail.com.
3 * Copyright 2011-2013, J��r��me Duval, korli@users.berlios.de.
4 * This file may be used under the terms of the MIT License.
5 */
6
7
8#include "DirectoryIterator.h"
9#include "CRCTable.h"
10
11
12//#define TRACE_BTRFS
13#ifdef TRACE_BTRFS
14#	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
15#else
16#	define TRACE(x...) ;
17#endif
18#	define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
19
20
21DirectoryIterator::DirectoryIterator(Inode* inode)
22	:
23	fOffset(0),
24	fInode(inode),
25	fIterator(NULL)
26{
27	btrfs_key key;
28	key.SetType(BTRFS_KEY_TYPE_DIR_INDEX);
29	key.SetObjectID(inode->ID());
30	key.SetOffset(BTREE_BEGIN);
31	fIterator = new(std::nothrow) TreeIterator(inode->GetVolume()->FSTree(),
32		key);
33}
34
35
36DirectoryIterator::~DirectoryIterator()
37{
38	delete fIterator;
39	fIterator = NULL;
40}
41
42
43status_t
44DirectoryIterator::InitCheck()
45{
46	return fIterator != NULL ? B_OK : B_NO_MEMORY;
47}
48
49
50status_t
51DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id)
52{
53	if (fOffset == 0) {
54		if (*_nameLength < 3)
55			return B_BUFFER_OVERFLOW;
56		*_nameLength = 2;
57		strlcpy(name, "..", *_nameLength + 1);
58		*_id = fInode->ID();
59		fOffset = 1;
60		return B_OK;
61	} else if (fOffset == 1) {
62		if (*_nameLength < 2)
63			return B_BUFFER_OVERFLOW;
64		*_nameLength = 1;
65		strlcpy(name, ".", *_nameLength + 1);
66		fOffset = 2;
67		if (fInode->ID() == BTRFS_FIRST_SUBVOLUME) {
68			*_id = fInode->ID();
69			return B_OK;
70		}
71		return fInode->FindParent(_id);
72	}
73
74	btrfs_dir_entry* entries;
75	uint32 entries_length;
76	status_t status = fIterator->GetNextEntry((void**)&entries, &entries_length);
77	if (status != B_OK)
78		return status;
79
80	btrfs_dir_entry* entry = entries;
81	uint16 current = 0;
82	while (current < entries_length) {
83		current += entry->Length();
84		break;
85		// TODO there could be several entries with the same name hash
86		entry = (btrfs_dir_entry*)((uint8*)entry + entry->Length());
87	}
88
89	size_t length = entry->NameLength();
90
91	TRACE("DirectoryIterator::GetNext() entries_length %ld name_length %d\n",
92		entries_length, entry->NameLength());
93
94	if (length + 1 > *_nameLength) {
95		free(entries);
96		return B_BUFFER_OVERFLOW;
97	}
98
99	memcpy(name, entry + 1, length);
100	name[length] = '\0';
101	*_nameLength = length;
102	*_id = entry->InodeID();
103	free(entries);
104
105	return B_OK;
106}
107
108
109status_t
110DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
111{
112	if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
113		if (strcmp(name, ".") == 0
114			|| fInode->ID() == BTRFS_FIRST_SUBVOLUME) {
115			*_id = fInode->ID();
116			return B_OK;
117		}
118		return fInode->FindParent(_id);
119	}
120
121	uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength);
122	btrfs_key key;
123	key.SetType(BTRFS_KEY_TYPE_DIR_ITEM);
124	key.SetObjectID(fInode->ID());
125	key.SetOffset(hash);
126	BTree::Path path(fInode->GetVolume()->FSTree());
127
128	btrfs_dir_entry* entries;
129	uint32 length;
130	status_t status = fInode->GetVolume()->FSTree()->FindExact(&path, key,
131		(void**)&entries, &length);
132	if (status != B_OK) {
133		TRACE("DirectoryIterator::Lookup(): Couldn't find entry with hash %" B_PRIu32
134			" \"%s\"\n", hash, name);
135		return status;
136	}
137
138	btrfs_dir_entry* entry = entries;
139	uint16 current = 0;
140	while (current < length) {
141		current += entry->Length();
142		break;
143		// TODO there could be several entries with the same name hash
144		entry = (btrfs_dir_entry*)((uint8*)entry + entry->Length());
145	}
146
147	TRACE("DirectoryIterator::Lookup() entries_length %ld name_length %d\n",
148		length, entry->NameLength());
149
150	*_id = entry->InodeID();
151	free(entries);
152
153	return B_OK;
154}
155
156
157status_t
158DirectoryIterator::Rewind()
159{
160	fIterator->Rewind();
161	fOffset = BTREE_BEGIN;
162	return B_OK;
163}
164
165