1/*
2 * Copyright 2009, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <errno.h>
8#include <fcntl.h>
9#include <limits.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15#include <map>
16#include <vector>
17
18#include <module.h>
19
20#include <disk_device_manager/ddm_modules.h>
21#include <disk_device_manager.h>
22
23
24struct partition_entry;
25typedef std::vector<partition_entry*> PartitionVector;
26
27struct partition_entry : partition_data {
28	partition_entry*	parent;
29	PartitionVector		children;
30	char				path[PATH_MAX];
31};
32
33typedef std::map<partition_id, partition_entry*> PartitionMap;
34typedef std::map<partition_id, disk_device_data*> DiskDeviceMap;
35
36
37static PartitionMap sPartitions;
38static DiskDeviceMap sDiskDevices;
39static partition_id sNextID = 1;
40
41
42static status_t
43create_disk_device(int fd, const char* path, partition_id* _id)
44{
45	partition_entry* partition = new partition_entry;
46	memset(partition, 0, sizeof(partition_entry));
47	partition->id = sNextID++;
48	strlcpy(partition->path, path, sizeof(partition->path));
49
50	disk_device_data* device = new disk_device_data;
51	device->id = partition->id;
52	device->path = partition->path;
53	device->flags = 0;
54
55	if (ioctl(fd, B_GET_GEOMETRY, &device->geometry) < 0) {
56		// maybe it's just a file
57		struct stat stat;
58		if (fstat(fd, &stat) < 0) {
59			delete partition;
60			delete device;
61			return errno;
62		}
63
64		uint32 blockSize = 512;
65		off_t blocks = stat.st_size / blockSize;
66		uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX;
67		if (heads == 0)
68			heads = 1;
69		device->geometry.bytes_per_sector = blockSize;
70	    device->geometry.sectors_per_track = 1;
71	    device->geometry.cylinder_count = blocks / heads;
72	    device->geometry.head_count = heads;
73	    device->geometry.device_type = B_DISK;
74	    device->geometry.removable = false;
75	    device->geometry.read_only = true;
76	    device->geometry.write_once = false;
77	}
78
79	partition->offset = 0;
80	partition->size = 1LL * device->geometry.head_count
81		* device->geometry.cylinder_count * device->geometry.sectors_per_track
82		* device->geometry.bytes_per_sector;
83	partition->block_size = device->geometry.bytes_per_sector;
84
85	sDiskDevices.insert(std::make_pair(partition->id, device));
86	sPartitions.insert(std::make_pair(partition->id, partition));
87
88	if (_id != NULL)
89		*_id = partition->id;
90
91	return B_OK;
92}
93
94
95static void
96print_disk_device(partition_id id)
97{
98	disk_device_data* data = get_disk_device(id);
99
100	printf("device ID %ld\n", id);
101	printf("  path                 %s\n", data->path);
102	printf("  geometry\n");
103	printf("    bytes per sector   %lu\n", data->geometry.bytes_per_sector);
104	printf("    sectors per track  %lu\n", data->geometry.sectors_per_track);
105	printf("    cylinder count     %lu\n", data->geometry.cylinder_count);
106	printf("    head count         %lu (size %lld bytes)\n",
107		data->geometry.head_count, 1LL * data->geometry.head_count
108			* data->geometry.cylinder_count * data->geometry.sectors_per_track
109			* data->geometry.bytes_per_sector);
110	printf("    device type        %d\n", data->geometry.device_type);
111	printf("    removable          %d\n", data->geometry.removable);
112	printf("    read only          %d\n", data->geometry.read_only);
113	printf("    write once         %d\n\n", data->geometry.write_once);
114}
115
116
117static void
118print_partition(partition_id id)
119{
120	partition_data* data = get_partition(id);
121
122	printf("partition ID %ld\n", id);
123	printf("  offset               %lld\n", data->offset);
124	printf("  size                 %lld\n", data->size);
125	printf("  content_size         %lld\n", data->content_size);
126	printf("  block_size           %lu\n", data->block_size);
127	printf("  child_count          %ld\n", data->child_count);
128	printf("  index                %ld\n", data->index);
129	printf("  status               %#lx\n", data->status);
130	printf("  flags                %#lx\n", data->flags);
131	printf("  name                 %s\n", data->name);
132	printf("  type                 %s\n", data->type);
133	printf("  content_name         %s\n", data->content_name);
134	printf("  content_type         %s\n", data->content_type);
135	printf("  parameters           %s\n", data->parameters);
136	printf("  content_parameters   %s\n", data->content_parameters);
137}
138
139
140status_t
141scan_partition(int fd, partition_id partitionID)
142{
143	partition_data* data = get_partition(partitionID);
144
145	void* list = open_module_list("partitioning_systems");
146	char name[PATH_MAX];
147	size_t nameSize = sizeof(name);
148	while (read_next_module_name(list, name, &nameSize) == B_OK) {
149		partition_module_info* moduleInfo;
150		if (get_module(name, (module_info**)&moduleInfo) == B_OK
151			&& moduleInfo->identify_partition != NULL) {
152			void* cookie;
153			float priority = moduleInfo->identify_partition(fd, data, &cookie);
154
155			printf("%s: %g\n", name, priority);
156
157			if (priority >= 0) {
158				// scan partitions
159				moduleInfo->scan_partition(fd, data, cookie);
160				moduleInfo->free_identify_partition_cookie(data, cookie);
161
162				free((char*)data->content_type);
163				data->content_type = strdup(moduleInfo->pretty_name);
164			}
165
166			put_module(name);
167		}
168		nameSize = sizeof(name);
169	}
170
171	return B_OK;
172}
173
174
175// #pragma mark - disk device manager API
176
177
178disk_device_data*
179write_lock_disk_device(partition_id partitionID)
180{
181	// TODO: we could check if the device is properly unlocked again
182	return get_disk_device(partitionID);
183}
184
185
186void
187write_unlock_disk_device(partition_id partitionID)
188{
189}
190
191
192disk_device_data*
193read_lock_disk_device(partition_id partitionID)
194{
195	// TODO: we could check if the device is properly unlocked again
196	return get_disk_device(partitionID);
197}
198
199
200void
201read_unlock_disk_device(partition_id partitionID)
202{
203}
204
205
206disk_device_data*
207get_disk_device(partition_id partitionID)
208{
209	DiskDeviceMap::iterator found = sDiskDevices.find(partitionID);
210	if (found == sDiskDevices.end())
211		return NULL;
212
213	return found->second;
214}
215
216
217partition_data*
218get_partition(partition_id partitionID)
219{
220	PartitionMap::iterator found = sPartitions.find(partitionID);
221	if (found == sPartitions.end())
222		return NULL;
223
224	return found->second;
225}
226
227
228partition_data*
229get_parent_partition(partition_id partitionID)
230{
231	PartitionMap::iterator found = sPartitions.find(partitionID);
232	if (found == sPartitions.end())
233		return NULL;
234
235	return found->second->parent;
236}
237
238
239partition_data*
240get_child_partition(partition_id partitionID, int32 index)
241{
242	PartitionMap::iterator found = sPartitions.find(partitionID);
243	if (found == sPartitions.end())
244		return NULL;
245
246	partition_entry* partition = found->second;
247
248	if (index < 0 || index >= (int32)partition->children.size())
249		return NULL;
250
251	return partition->children[index];
252}
253
254
255int
256open_partition(partition_id partitionID, int openMode)
257{
258	return -1;
259}
260
261
262partition_data*
263create_child_partition(partition_id partitionID, int32 index, off_t offset,
264	off_t size, partition_id childID)
265{
266	PartitionMap::iterator found = sPartitions.find(partitionID);
267	if (found == sPartitions.end())
268		return NULL;
269
270	partition_entry* parent = found->second;
271
272	partition_entry* child = new partition_entry();
273	memset(child, 0, sizeof(partition_entry));
274
275	child->id = sNextID++;
276	child->offset = offset;
277	child->size = size;
278	child->index = parent->children.size();
279
280	parent->children.push_back(child);
281	parent->child_count++;
282	sPartitions.insert(std::make_pair(child->id, child));
283
284	printf("  new partition ID %ld (child of %ld)\n", child->id, parent->id);
285	return child;
286}
287
288
289bool
290delete_partition(partition_id partitionID)
291{
292	// TODO
293	return false;
294}
295
296
297void
298partition_modified(partition_id partitionID)
299{
300	// TODO: implemented
301}
302
303
304status_t
305scan_partition(partition_id partitionID)
306{
307	PartitionMap::iterator found = sPartitions.find(partitionID);
308	if (found == sPartitions.end())
309		return B_ENTRY_NOT_FOUND;
310
311	if (sDiskDevices.find(partitionID) == sDiskDevices.end()) {
312		// TODO: we would need to fake child partitons
313		return B_NOT_SUPPORTED;
314	}
315
316	partition_entry* partition = found->second;
317	int fd = open(partition->path, O_RDONLY);
318	if (fd < 0)
319		return errno;
320
321	scan_partition(fd, partitionID);
322	void* list = open_module_list("partitioning_systems");
323	char name[PATH_MAX];
324	size_t nameSize = sizeof(name);
325	while (read_next_module_name(list, name, &nameSize) == B_OK) {
326		puts(name);
327		nameSize = sizeof(name);
328	}
329	return B_ERROR;
330}
331
332
333bool
334update_disk_device_job_progress(disk_job_id jobID, float progress)
335{
336	return false;
337}
338
339
340bool
341set_disk_device_job_error_message(disk_job_id jobID, const char* message)
342{
343#if 0
344	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
345	if (ManagerLocker locker = manager) {
346		if (KDiskDeviceJob* job = manager->FindJob(jobID)) {
347			job->SetErrorMessage(message);
348			return true;
349		}
350	}
351#endif
352	return false;
353}
354
355
356// #pragma mark -
357
358
359static void
360usage()
361{
362	extern const char* __progname;
363
364	fprintf(stderr, "usage: %s <device>\n"
365		"Must be started from the top-level Haiku directory to find its "
366		"add-ons.\n", __progname);
367	exit(1);
368}
369
370
371int
372main(int argc, char** argv)
373{
374	if (argc != 2)
375		usage();
376
377	const char* deviceName = argv[1];
378
379	int device = open(deviceName, O_RDONLY);
380	if (device < 0) {
381		fprintf(stderr, "Could not open device \"%s\": %s\n", deviceName,
382			strerror(errno));
383		return 1;
384	}
385
386	partition_id id;
387	status_t status = create_disk_device(device, deviceName, &id);
388	if (status != B_OK) {
389		fprintf(stderr, "Could not get device size \"%s\": %s\n", deviceName,
390			strerror(status));
391		return 1;
392	}
393
394	print_disk_device(id);
395	scan_partition(device, id);
396
397	PartitionMap::iterator iterator = sPartitions.begin();
398	for (; iterator != sPartitions.end(); iterator++) {
399		print_partition(iterator->first);
400	}
401
402	close(device);
403	return 0;
404}
405
406