1// Volume.cpp
2
3#include <new>
4
5#include <string.h>
6#include <sys/stat.h>
7
8#include <Directory.h>
9#include <fs_info.h>
10#include <HashMap.h>
11
12#include "Directory.h"
13#include "FDManager.h"
14#include "Entry.h"
15#include "Node.h"
16#include "Volume.h"
17
18// NodeMap
19struct Volume::NodeMap : HashMap<HashKey64<ino_t>, Node*> {
20};
21
22// EntryKey
23//
24// NOTE: This class doesn't make a copy of the name string it is constructed
25// with. So, when entering the key in a map, one must make sure, that the
26// string stays valid as long as the entry is in the map.
27struct Volume::EntryKey {
28	EntryKey() {}
29
30	EntryKey(ino_t directoryID, const char* name)
31		: directoryID(directoryID),
32		  name(name)
33	{
34	}
35
36	EntryKey(Entry* entry)
37		: directoryID(entry->GetDirectoryID()),
38		  name(entry->GetName())
39	{
40	}
41
42	EntryKey(const EntryKey& other)
43		: directoryID(other.directoryID),
44		  name(other.name)
45	{
46	}
47
48	uint32 GetHashCode() const
49	{
50		uint32 hash = (uint32)directoryID;
51		hash = 31 * hash + (uint32)(directoryID >> 32);
52		hash = 31 * hash + string_hash(name);
53		return hash;
54	}
55
56	EntryKey& operator=(const EntryKey& other)
57	{
58		directoryID = other.directoryID;
59		name = other.name;
60		return *this;
61	}
62
63	bool operator==(const EntryKey& other) const
64	{
65		if (directoryID != other.directoryID)
66			return false;
67
68		if (name)
69			return (other.name && strcmp(name, other.name) == 0);
70
71		return !other.name;
72	}
73
74	bool operator!=(const EntryKey& other) const
75	{
76		return !(*this == other);
77	}
78
79	ino_t		directoryID;
80	const char*	name;
81};
82
83// EntryMap
84struct Volume::EntryMap : HashMap<EntryKey, Entry*> {
85};
86
87
88// constructor
89Volume::Volume(dev_t id)
90	: fID(id),
91	  fRootDir(NULL),
92	  fFSFlags(0),
93	  fNodes(NULL),
94	  fEntries(NULL)
95{
96}
97
98// destructor
99Volume::~Volume()
100{
101	// delete all entries
102	if (fEntries) {
103		for (EntryMap::Iterator it = fEntries->GetIterator(); it.HasNext();) {
104			Entry* entry = it.Next().value;
105			delete entry;
106		}
107		delete fEntries;
108	}
109
110	// delete all nodes
111	if (fNodes) {
112		// remove the root dir -- we delete it separately
113		if (fRootDir)
114			RemoveNode(fRootDir);
115
116		// delete the nodes
117		for (NodeMap::Iterator it = fNodes->GetIterator(); it.HasNext();) {
118			Node* node = it.Next().value;
119			delete node;
120		}
121
122		delete fNodes;
123	}
124
125	// delete the root dir
126	delete fRootDir;
127}
128
129// Init
130status_t
131Volume::Init()
132{
133	// create the node map
134	fNodes = new(std::nothrow) NodeMap;
135	if (!fNodes)
136		return B_NO_MEMORY;
137	if (fNodes->InitCheck() != B_OK)
138		return fNodes->InitCheck();
139
140	// create the entry map
141	fEntries = new(std::nothrow) EntryMap;
142	if (!fEntries)
143		return B_NO_MEMORY;
144	if (fEntries->InitCheck() != B_OK)
145		return fEntries->InitCheck();
146
147	// get a volume info
148	fs_info info;
149	status_t error = fs_stat_dev(fID, &info);
150	if (error != B_OK)
151		return error;
152	fFSFlags = info.flags;
153
154	// open the root directory
155	node_ref rootRef;
156	rootRef.device = fID;
157	rootRef.node = info.root;
158	BDirectory rootDir;
159	error = FDManager::SetDirectory(&rootDir, &rootRef);
160	if (error != B_OK)
161		return error;
162
163	// stat the root dir
164	struct stat st;
165	error = rootDir.GetStat(&st);
166	if (error != B_OK)
167		return error;
168
169	// create the root dir
170	fRootDir = new(std::nothrow) Directory(this, st);
171	if (!fRootDir)
172		return B_NO_MEMORY;
173
174	// the root dir is added by the VolumeManager
175
176	return B_OK;
177}
178
179// GetID
180dev_t
181Volume::GetID() const
182{
183	return fID;
184}
185
186// GetRootDirectory
187Directory*
188Volume::GetRootDirectory() const
189{
190	return fRootDir;
191}
192
193// GetRootID
194ino_t
195Volume::GetRootID() const
196{
197	return fRootDir->GetID();
198}
199
200// KnowsQuery
201bool
202Volume::KnowsQuery() const
203{
204	return (fFSFlags & B_FS_HAS_QUERY);
205}
206
207
208// AddNode
209status_t
210Volume::AddNode(Node* node)
211{
212	if (!node || node->GetVolume() != this || GetNode(node->GetID()))
213		return B_BAD_VALUE;
214
215	return fNodes->Put(node->GetID(), node);
216}
217
218// RemoveNode
219bool
220Volume::RemoveNode(Node* node)
221{
222	if (node && GetNode(node->GetID()) == node) {
223		fNodes->Remove(node->GetID());
224		return true;
225	}
226
227	return false;
228}
229
230// GetNode
231Node*
232Volume::GetNode(ino_t nodeID)
233{
234	return fNodes->Get(nodeID);
235}
236
237// GetFirstNode
238Node*
239Volume::GetFirstNode() const
240{
241	NodeMap::Iterator it = fNodes->GetIterator();
242	if (it.HasNext())
243		return it.Next().value;
244	return NULL;
245}
246
247// AddEntry
248status_t
249Volume::AddEntry(Entry* entry)
250{
251	if (!entry || entry->GetVolume() != this
252		|| GetEntry(entry->GetDirectoryID(), entry->GetName())) {
253		return B_BAD_VALUE;
254	}
255
256	return fEntries->Put(EntryKey(entry), entry);
257}
258
259// RemoveEntry
260bool
261Volume::RemoveEntry(Entry* entry)
262{
263	if (entry && GetEntry(entry->GetDirectoryID(), entry->GetName()) == entry) {
264		fEntries->Remove(EntryKey(entry));
265		return true;
266	}
267
268	return false;
269}
270
271// GetEntry
272Entry*
273Volume::GetEntry(ino_t dirID, const char* name)
274{
275	return fEntries->Get(EntryKey(dirID, name));
276}
277
278// GetFirstEntry
279Entry*
280Volume::GetFirstEntry() const
281{
282	EntryMap::Iterator it = fEntries->GetIterator();
283	if (it.HasNext())
284		return it.Next().value;
285	return NULL;
286}
287
288