1/*
2 * Copyright 2003-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Ingo Weinhold, ingo_weinhold@gmx.de
7 *		Axel Dörfler, axeld@pinc-software.de
8 */
9
10/*! \file ddm_userland_interface.cpp
11
12 	\brief Interface for userspace calls.
13*/
14
15#include <ddm_userland_interface.h>
16
17#include <stdlib.h>
18
19#include <AutoDeleter.h>
20#include <fs/KPath.h>
21#include <KDiskDevice.h>
22#include <KDiskDeviceManager.h>
23#include <KDiskDeviceUtils.h>
24#include <KDiskSystem.h>
25#include <KFileDiskDevice.h>
26#include <syscall_args.h>
27
28#include "UserDataWriter.h"
29
30using namespace BPrivate::DiskDevice;
31
32// debugging
33#define ERROR(x)
34
35
36// TODO: Replace all instances, when it has been decided how to handle
37// notifications during jobs.
38#define DUMMY_JOB_ID	0
39
40
41// TODO: Add user address checks and check return values of user_memcpy()!
42
43
44/*! \brief Wrapper around user_strlcpy() that returns a status_t
45	indicating appropriate success or failure.
46
47	\param allowTruncation If \c true, does not return an error if
48	       \a from is longer than \to. If \c false, returns \c B_NAME_TOO_LONG
49	       if \a from is longer than \to.
50*/
51static status_t
52ddm_strlcpy(char *to, const char *from, size_t size,
53	bool allowTruncation = false)
54{
55	ssize_t fromLen = user_strlcpy(to, from, size);
56	if (fromLen < 0)
57		return fromLen;
58	if ((size_t)fromLen >= size && !allowTruncation)
59		return B_NAME_TOO_LONG;
60	return B_OK;
61}
62
63
64template<typename Type>
65static inline status_t
66copy_from_user_value(Type& value, const Type* userValue)
67{
68	if (!userValue)
69		return B_BAD_VALUE;
70
71	if (!IS_USER_ADDRESS(userValue))
72		return B_BAD_ADDRESS;
73
74	return user_memcpy(&value, userValue, sizeof(Type));
75}
76
77
78template<typename Type>
79static inline status_t
80copy_to_user_value(Type* userValue, const Type& value)
81{
82	if (!userValue)
83		return B_BAD_VALUE;
84
85	if (!IS_USER_ADDRESS(userValue))
86		return B_BAD_ADDRESS;
87
88	return user_memcpy(userValue, &value, sizeof(Type));
89}
90
91
92template<bool kAllowsNull>
93struct UserStringParameter {
94	char*	value;
95
96	inline UserStringParameter()
97		: value(NULL)
98	{
99	}
100
101	inline ~UserStringParameter()
102	{
103		free(value);
104	}
105
106	inline status_t Init(const char* userValue, size_t maxSize)
107	{
108		if (userValue == NULL) {
109			if (!kAllowsNull)
110				return B_BAD_VALUE;
111
112			return B_OK;
113		}
114
115		if (!IS_USER_ADDRESS(userValue))
116			return B_BAD_ADDRESS;
117
118		value = (char*)malloc(maxSize);
119		if (value == NULL)
120			return B_NO_MEMORY;
121
122		ssize_t bytesCopied = user_strlcpy(value, userValue, maxSize);
123		if (bytesCopied < 0)
124			return bytesCopied;
125
126		if ((size_t)bytesCopied >= maxSize)
127			return B_BUFFER_OVERFLOW;
128
129		return B_OK;
130	}
131
132	inline operator const char*()
133	{
134		return value;
135	}
136
137	inline operator char*()
138	{
139		return value;
140	}
141};
142
143
144#if 0
145static void
146move_descendants(KPartition *partition, off_t moveBy)
147{
148	if (!partition)
149		return;
150	partition->SetOffset(partition->Offset() + moveBy);
151	// move children
152	for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++)
153		move_descendants(child, moveBy);
154}
155
156
157static status_t
158move_descendants_contents(KPartition *partition)
159{
160	if (!partition)
161		return B_BAD_VALUE;
162	// implicit content disk system changes
163	KDiskSystem *diskSystem = partition->DiskSystem();
164	if (diskSystem || partition->AlgorithmData()) {
165		status_t error = diskSystem->ShadowPartitionChanged(partition,
166			NULL, B_PARTITION_MOVE);
167		if (error != B_OK)
168			return error;
169	}
170	// move children's contents
171	for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++) {
172		status_t error = move_descendants_contents(child);
173		if (error != B_OK)
174			return error;
175	}
176	return B_OK;
177}
178#endif // 0
179
180
181partition_id
182_user_get_next_disk_device_id(int32 *_cookie, size_t *neededSize)
183{
184	if (!_cookie)
185		return B_BAD_VALUE;
186	int32 cookie;
187	user_memcpy(&cookie, _cookie, sizeof(cookie));
188
189	partition_id id = B_ENTRY_NOT_FOUND;
190	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
191	// get the next device
192	if (KDiskDevice *device = manager->RegisterNextDevice(&cookie)) {
193		PartitionRegistrar _(device, true);
194		id = device->ID();
195		if (neededSize) {
196			if (DeviceReadLocker locker = device) {
197				// get the needed size
198				UserDataWriter writer;
199				device->WriteUserData(writer);
200				status_t status = copy_to_user_value(neededSize,
201					writer.AllocatedSize());
202				if (status != B_OK)
203					return status;
204			} else {
205				id = B_ERROR;
206			}
207		}
208	}
209	user_memcpy(_cookie, &cookie, sizeof(cookie));
210	return id;
211}
212
213
214partition_id
215_user_find_disk_device(const char *_filename, size_t *neededSize)
216{
217	UserStringParameter<false> filename;
218	status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
219	if (error != B_OK)
220		return error;
221
222	partition_id id = B_ENTRY_NOT_FOUND;
223	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
224	// find the device
225	if (KDiskDevice *device = manager->RegisterDevice(filename)) {
226		PartitionRegistrar _(device, true);
227		id = device->ID();
228		if (neededSize) {
229			if (DeviceReadLocker locker = device) {
230				// get the needed size
231				UserDataWriter writer;
232				device->WriteUserData(writer);
233				error = copy_to_user_value(neededSize, writer.AllocatedSize());
234				if (error != B_OK)
235					return error;
236			} else
237				return B_ERROR;
238		}
239	}
240	return id;
241}
242
243
244partition_id
245_user_find_partition(const char *_filename, size_t *neededSize)
246{
247	UserStringParameter<false> filename;
248	status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
249	if (error != B_OK)
250		return error;
251
252	partition_id id = B_ENTRY_NOT_FOUND;
253	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
254	// find the partition
255	if (KPartition *partition = manager->RegisterPartition(filename)) {
256		PartitionRegistrar _(partition, true);
257		id = partition->ID();
258		if (neededSize) {
259			// get and lock the partition's device
260			KDiskDevice *device = manager->RegisterDevice(partition->ID(),
261				false);
262			if (!device)
263				return B_ENTRY_NOT_FOUND;
264			PartitionRegistrar _2(device, true);
265			if (DeviceReadLocker locker = device) {
266				// get the needed size
267				UserDataWriter writer;
268				device->WriteUserData(writer);
269				error = copy_to_user_value(neededSize, writer.AllocatedSize());
270				if (error != B_OK)
271					return error;
272			} else
273				return B_ERROR;
274		}
275	}
276	return id;
277}
278
279
280partition_id
281_user_find_file_disk_device(const char *_filename, size_t *neededSize)
282{
283	UserStringParameter<false> filename;
284	status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
285	if (error != B_OK)
286		return error;
287
288	KPath path(filename, true);
289
290	partition_id id = B_ENTRY_NOT_FOUND;
291	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
292	// find the device
293	if (KFileDiskDevice* device = manager->RegisterFileDevice(path.Path())) {
294		PartitionRegistrar _(device, true);
295		id = device->ID();
296		if (neededSize) {
297			if (DeviceReadLocker locker = device) {
298				// get the needed size
299				UserDataWriter writer;
300				device->WriteUserData(writer);
301				error = copy_to_user_value(neededSize, writer.AllocatedSize());
302				if (error != B_OK)
303					return error;
304			} else
305				return B_ERROR;
306		}
307	}
308	return id;
309}
310
311
312/*!	\brief Writes data describing the disk device identified by ID and all
313		   its partitions into the supplied buffer.
314
315	The function passes the buffer size required to hold the data back
316	through the \a _neededSize parameter, if the device could be found at
317	least and no serious error occured. If fails with \c B_BUFFER_OVERFLOW,
318	if the supplied buffer is too small or a \c NULL buffer is supplied
319	(and \c bufferSize is 0).
320
321	The device is identified by \a id. If \a deviceOnly is \c true, then
322	it must be the ID of a disk device, otherwise the disk device is
323	chosen, on which the partition \a id refers to resides.
324
325	\param id The ID of an arbitrary partition on the disk device (including
326		   the disk device itself), whose data shall be returned
327		   (if \a deviceOnly is \c false), or the ID of the disk device
328		   itself (if \a deviceOnly is true).
329	\param deviceOnly Specifies whether only IDs of disk devices (\c true),
330		   or also IDs of partitions (\c false) are accepted for \a id.
331	\param buffer The buffer into which the disk device data shall be written.
332		   May be \c NULL.
333	\param bufferSize The size of \a buffer.
334	\param _neededSize Pointer to a variable into which the actually needed
335		   buffer size is written. May be \c NULL.
336	\return
337	- \c B_OK: Everything went fine. The device was found and, if not \c NULL,
338	  in \a _neededSize the actually needed buffer size is returned. And
339	  \a buffer will contain the disk device data.
340	- \c B_BAD_VALUE: \c NULL \a buffer, but not 0 \a bufferSize.
341	- \c B_BUFFER_OVERFLOW: The supplied buffer was too small. \a _neededSize,
342	  if not \c NULL, will contain the required buffer size.
343	- \c B_NO_MEMORY: Insufficient memory to complete the operation.
344	- \c B_ENTRY_NOT_FOUND: \a id is no valid disk device ID (if \a deviceOnly
345	  is \c true) or not even a valid partition ID (if \a deviceOnly is
346	  \c false).
347	- \c B_ERROR: An unexpected error occured.
348	- another error code...
349*/
350status_t
351_user_get_disk_device_data(partition_id id, bool deviceOnly,
352	user_disk_device_data *buffer, size_t bufferSize, size_t *_neededSize)
353{
354	if (!buffer && bufferSize > 0)
355		return B_BAD_VALUE;
356	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
357	// get the device
358	if (KDiskDevice *device = manager->RegisterDevice(id, deviceOnly)) {
359		PartitionRegistrar _(device, true);
360		if (DeviceReadLocker locker = device) {
361			// do a dry run first to get the needed size
362			UserDataWriter writer;
363			device->WriteUserData(writer);
364			size_t neededSize = writer.AllocatedSize();
365			if (_neededSize) {
366				status_t error = copy_ref_var_to_user(neededSize, _neededSize);
367				if (error != B_OK)
368					return error;
369			}
370			// if no buffer has been supplied or the buffer is too small,
371			// then we're done
372			if (!buffer || bufferSize < neededSize)
373				return B_BUFFER_OVERFLOW;
374			// otherwise allocate a kernel buffer
375			user_disk_device_data *kernelBuffer
376				= static_cast<user_disk_device_data*>(malloc(neededSize));
377			if (!kernelBuffer)
378				return B_NO_MEMORY;
379			MemoryDeleter deleter(kernelBuffer);
380			// write the device data into the buffer
381			writer.SetTo(kernelBuffer, bufferSize);
382			device->WriteUserData(writer);
383			// sanity check
384			if (writer.AllocatedSize() != neededSize) {
385				ERROR(("Size of written disk device user data changed from "
386					   "%lu to %lu while device was locked!\n"));
387				return B_ERROR;
388			}
389			// relocate
390			status_t error = writer.Relocate(buffer);
391			if (error != B_OK)
392				return error;
393			// copy out
394			if (buffer)
395				return user_memcpy(buffer, kernelBuffer, neededSize);
396		} else
397			return B_ERROR;
398	}
399	return B_ENTRY_NOT_FOUND;
400}
401
402
403partition_id
404_user_register_file_device(const char *_filename)
405{
406	UserStringParameter<false> filename;
407	status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
408	if (error != B_OK)
409		return error;
410
411	KPath path(filename, true);
412
413	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
414	if (ManagerLocker locker = manager) {
415		if (KFileDiskDevice *device = manager->FindFileDevice(path.Path()))
416			return device->ID();
417		return manager->CreateFileDevice(path.Path());
418	}
419	return B_ERROR;
420}
421
422
423status_t
424_user_unregister_file_device(partition_id deviceID, const char *_filename)
425{
426	if (deviceID < 0 && !_filename)
427		return B_BAD_VALUE;
428	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
429	if (deviceID >= 0) {
430		return manager->DeleteFileDevice(deviceID);
431	} else {
432		UserStringParameter<false> filename;
433		status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
434		if (error != B_OK)
435			return error;
436
437		return manager->DeleteFileDevice(filename);
438	}
439}
440
441
442status_t
443_user_get_file_disk_device_path(partition_id id, char* buffer,
444	size_t bufferSize)
445{
446	if (id < 0 || buffer == NULL || bufferSize == 0)
447		return B_BAD_VALUE;
448
449	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
450
451	if (KDiskDevice *device = manager->RegisterDevice(id, true)) {
452		PartitionRegistrar _(device, true);
453		if (DeviceReadLocker locker = device) {
454			KFileDiskDevice* fileDevice
455				= dynamic_cast<KFileDiskDevice*>(device);
456			if (fileDevice == NULL)
457				return B_BAD_VALUE;
458
459			ssize_t copied = user_strlcpy(buffer, fileDevice->FilePath(),
460				bufferSize);
461			if (copied < 0)
462				return copied;
463			return (size_t)copied < bufferSize ? B_OK : B_BUFFER_OVERFLOW;
464		}
465	}
466
467	return B_ERROR;
468}
469
470
471status_t
472_user_get_disk_system_info(disk_system_id id, user_disk_system_info *_info)
473{
474	if (!_info)
475		return B_BAD_VALUE;
476	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
477	if (ManagerLocker locker = manager) {
478		if (KDiskSystem *diskSystem = manager->FindDiskSystem(id)) {
479			user_disk_system_info info;
480			diskSystem->GetInfo(&info);
481			user_memcpy(_info, &info, sizeof(info));
482			return B_OK;
483		}
484	}
485	return B_ENTRY_NOT_FOUND;
486}
487
488
489status_t
490_user_get_next_disk_system_info(int32 *_cookie, user_disk_system_info *_info)
491{
492	if (!_cookie || !_info)
493		return B_BAD_VALUE;
494	int32 cookie;
495	user_memcpy(&cookie, _cookie, sizeof(cookie));
496	status_t result = B_ENTRY_NOT_FOUND;
497	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
498	if (ManagerLocker locker = manager) {
499		if (KDiskSystem *diskSystem = manager->NextDiskSystem(&cookie)) {
500			user_disk_system_info info;
501			diskSystem->GetInfo(&info);
502			user_memcpy(_info, &info, sizeof(info));
503			result = B_OK;
504		}
505	}
506	user_memcpy(_cookie, &cookie, sizeof(cookie));
507	return result;
508}
509
510
511status_t
512_user_find_disk_system(const char *_name, user_disk_system_info *_info)
513{
514	if (!_name || !_info)
515		return B_BAD_VALUE;
516	char name[B_DISK_SYSTEM_NAME_LENGTH];
517	status_t error = ddm_strlcpy(name, _name, B_DISK_SYSTEM_NAME_LENGTH);
518	if (error)
519		return error;
520	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
521	if (ManagerLocker locker = manager) {
522		if (KDiskSystem *diskSystem = manager->FindDiskSystem(name)) {
523			user_disk_system_info info;
524			diskSystem->GetInfo(&info);
525			user_memcpy(_info, &info, sizeof(info));
526			return B_OK;
527		}
528	}
529	return B_ENTRY_NOT_FOUND;
530}
531
532
533status_t
534_user_defragment_partition(partition_id partitionID, int32* _changeCounter)
535{
536	// copy parameters in
537	int32 changeCounter;
538
539	status_t error;
540	if ((error = copy_from_user_value(changeCounter, _changeCounter)) != B_OK)
541		return error;
542
543	// get the partition
544	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
545	KPartition* partition = manager->WriteLockPartition(partitionID);
546	if (!partition)
547		return B_ENTRY_NOT_FOUND;
548
549	PartitionRegistrar registrar1(partition, true);
550	PartitionRegistrar registrar2(partition->Device(), true);
551	DeviceWriteLocker locker(partition->Device(), true);
552
553	// check change counter
554	if (changeCounter != partition->ChangeCounter())
555		return B_BAD_VALUE;
556
557	// the partition must be initialized
558	KDiskSystem* diskSystem = partition->DiskSystem();
559	if (!diskSystem)
560		return B_BAD_VALUE;
561
562	// mark the partition busy and unlock
563	if (!partition->CheckAndMarkBusy(false))
564		return B_BUSY;
565	locker.Unlock();
566
567	// defragment
568	error = diskSystem->Defragment(partition, DUMMY_JOB_ID);
569
570	// re-lock and unmark busy
571	locker.Lock();
572	partition->UnmarkBusy(false);
573
574	if (error != B_OK)
575		return error;
576
577	// return change counter
578	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
579			!= B_OK) {
580		return error;
581	}
582
583	return B_OK;
584}
585
586
587status_t
588_user_repair_partition(partition_id partitionID, int32* _changeCounter,
589	bool checkOnly)
590{
591	// copy parameters in
592	int32 changeCounter;
593
594	status_t error;
595	if ((error = copy_from_user_value(changeCounter, _changeCounter)) != B_OK)
596		return error;
597
598	// get the partition
599	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
600	KPartition* partition = manager->WriteLockPartition(partitionID);
601	if (!partition)
602		return B_ENTRY_NOT_FOUND;
603
604	PartitionRegistrar registrar1(partition, true);
605	PartitionRegistrar registrar2(partition->Device(), true);
606	DeviceWriteLocker locker(partition->Device(), true);
607
608	// check change counter
609	if (changeCounter != partition->ChangeCounter())
610		return B_BAD_VALUE;
611
612	// the partition must be initialized
613	KDiskSystem* diskSystem = partition->DiskSystem();
614	if (!diskSystem)
615		return B_BAD_VALUE;
616
617	// mark the partition busy and unlock
618	if (!partition->CheckAndMarkBusy(false))
619		return B_BUSY;
620	locker.Unlock();
621
622	// repair/check
623	error = diskSystem->Repair(partition, checkOnly, DUMMY_JOB_ID);
624
625	// re-lock and unmark busy
626	locker.Lock();
627	partition->UnmarkBusy(false);
628
629	if (error != B_OK)
630		return error;
631
632	// return change counter
633	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
634			!= B_OK) {
635		return error;
636	}
637
638	return B_OK;
639}
640
641
642status_t
643_user_resize_partition(partition_id partitionID, int32* _changeCounter,
644	partition_id childID, int32* _childChangeCounter, off_t size,
645	off_t contentSize)
646{
647	// copy parameters in
648	int32 changeCounter;
649	int32 childChangeCounter;
650
651	status_t error;
652	if ((error = copy_from_user_value(changeCounter, _changeCounter)) != B_OK
653		|| (error = copy_from_user_value(childChangeCounter,
654			_childChangeCounter)) != B_OK) {
655		return error;
656	}
657
658	// get the partition
659	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
660	KPartition* partition = manager->WriteLockPartition(partitionID);
661	if (!partition)
662		return B_ENTRY_NOT_FOUND;
663
664	PartitionRegistrar registrar1(partition, true);
665	PartitionRegistrar registrar2(partition->Device(), true);
666	DeviceWriteLocker locker(partition->Device(), true);
667
668	// register child
669	KPartition* child = manager->RegisterPartition(childID);
670	if (!child)
671		return B_ENTRY_NOT_FOUND;
672
673	PartitionRegistrar registrar3(child, true);
674
675	// check change counters
676	if (changeCounter != partition->ChangeCounter()
677		|| childChangeCounter != child->ChangeCounter()) {
678		return B_BAD_VALUE;
679	}
680
681	// the partition must be initialized
682	KDiskSystem* diskSystem = partition->DiskSystem();
683	if (!diskSystem)
684		return B_BAD_VALUE;
685
686	// child must indeed be a child of partition
687	if (child->Parent() != partition)
688		return B_BAD_VALUE;
689
690	// check sizes
691	if (size < 0 || contentSize < 0 || size < contentSize
692		|| size > partition->ContentSize()) {
693		return B_BAD_VALUE;
694	}
695
696	// mark the partitions busy and unlock
697	if (partition->IsBusy() || child->IsBusy())
698		return B_BUSY;
699	partition->SetBusy(true);
700	child->SetBusy(true);
701	locker.Unlock();
702
703	// resize contents first, if shrinking
704	if (child->DiskSystem() && contentSize < child->ContentSize())
705		error = child->DiskSystem()->Resize(child, contentSize, DUMMY_JOB_ID);
706
707	// resize the partition
708	if (error == B_OK && size != child->Size())
709		error = diskSystem->ResizeChild(child, size, DUMMY_JOB_ID);
710
711	// resize contents last, if growing
712	if (error == B_OK && child->DiskSystem()
713		&& contentSize > child->ContentSize()) {
714		error = child->DiskSystem()->Resize(child, contentSize, DUMMY_JOB_ID);
715	}
716
717	// re-lock and unmark busy
718	locker.Lock();
719	partition->SetBusy(false);
720	child->SetBusy(false);
721
722	if (error != B_OK)
723		return error;
724
725	// return change counters
726	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
727			!= B_OK
728		|| (error = copy_to_user_value(_childChangeCounter,
729			child->ChangeCounter())) != B_OK) {
730		return error;
731	}
732
733	return B_OK;
734}
735
736
737status_t
738_user_move_partition(partition_id partitionID, int32* changeCounter,
739	partition_id childID, int32* childChangeCounter, off_t newOffset,
740	partition_id* descendantIDs, int32* descendantChangeCounters,
741	int32 descendantCount)
742{
743#if 0
744	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
745	// get the partition
746	KPartition *partition = manager->WriteLockPartition(partitionID);
747	if (!partition)
748		return B_ENTRY_NOT_FOUND;
749	PartitionRegistrar registrar1(partition, true);
750	PartitionRegistrar registrar2(partition->Device(), true);
751	DeviceWriteLocker locker(partition->Device(), true);
752	// check the new offset
753	if (newOffset == partition->Offset())
754		return B_OK;
755	off_t proposedOffset = newOffset;
756	status_t error = validate_move_partition(partition, changeCounter,
757		&proposedOffset, true);
758	if (error != B_OK)
759		return error;
760	if (proposedOffset != newOffset)
761		return B_BAD_VALUE;
762	// new offset is fine -- move the thing
763	off_t moveBy = newOffset - partition->Offset();
764	move_descendants(partition, moveBy);
765	partition->Changed(B_PARTITION_CHANGED_OFFSET);
766	// implicit partitioning system changes
767	error = partition->Parent()->DiskSystem()->ShadowPartitionChanged(
768		partition->Parent(), partition, B_PARTITION_MOVE_CHILD);
769	if (error != B_OK)
770		return error;
771	// implicit descendants' content disk system changes
772	return move_descendants_contents(partition);
773#endif
774return B_BAD_VALUE;
775}
776
777
778status_t
779_user_set_partition_name(partition_id partitionID, int32* _changeCounter,
780	partition_id childID, int32* _childChangeCounter, const char* _name)
781{
782	// copy parameters in
783	UserStringParameter<false> name;
784	int32 changeCounter;
785	int32 childChangeCounter;
786
787	status_t error;
788	if ((error = name.Init(_name, B_DISK_DEVICE_NAME_LENGTH)) != B_OK
789		|| (error = copy_from_user_value(changeCounter, _changeCounter))
790			!= B_OK
791		|| (error = copy_from_user_value(childChangeCounter,
792			_childChangeCounter)) != B_OK) {
793		return error;
794	}
795
796	// get the partition
797	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
798	KPartition* partition = manager->WriteLockPartition(partitionID);
799	if (!partition)
800		return B_ENTRY_NOT_FOUND;
801
802	PartitionRegistrar registrar1(partition, true);
803	PartitionRegistrar registrar2(partition->Device(), true);
804	DeviceWriteLocker locker(partition->Device(), true);
805
806	// register child
807	KPartition* child = manager->RegisterPartition(childID);
808	if (!child)
809		return B_ENTRY_NOT_FOUND;
810
811	PartitionRegistrar registrar3(child, true);
812
813	// check change counters
814	if (changeCounter != partition->ChangeCounter()
815		|| childChangeCounter != child->ChangeCounter()) {
816		return B_BAD_VALUE;
817	}
818
819	// the partition must be initialized
820	KDiskSystem* diskSystem = partition->DiskSystem();
821	if (!diskSystem)
822		return B_BAD_VALUE;
823
824	// child must indeed be a child of partition
825	if (child->Parent() != partition)
826		return B_BAD_VALUE;
827
828	// mark the partitions busy and unlock
829	if (partition->IsBusy() || child->IsBusy())
830		return B_BUSY;
831	partition->SetBusy(true);
832	child->SetBusy(true);
833	locker.Unlock();
834
835	// set the child name
836	error = diskSystem->SetName(child, name.value, DUMMY_JOB_ID);
837
838	// re-lock and unmark busy
839	locker.Lock();
840	partition->SetBusy(false);
841	child->SetBusy(false);
842
843	if (error != B_OK)
844		return error;
845
846	// return change counters
847	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
848			!= B_OK
849		|| (error = copy_to_user_value(_childChangeCounter,
850			child->ChangeCounter())) != B_OK) {
851		return error;
852	}
853
854	return B_OK;
855}
856
857
858status_t
859_user_set_partition_content_name(partition_id partitionID,
860	int32* _changeCounter, const char* _name)
861{
862	// copy parameters in
863	UserStringParameter<true> name;
864	int32 changeCounter;
865
866	status_t error;
867	if ((error = name.Init(_name, B_DISK_DEVICE_NAME_LENGTH)) != B_OK
868		|| (error = copy_from_user_value(changeCounter, _changeCounter))
869			!= B_OK) {
870		return error;
871	}
872
873	// get the partition
874	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
875	KPartition* partition = manager->WriteLockPartition(partitionID);
876	if (!partition)
877		return B_ENTRY_NOT_FOUND;
878
879	PartitionRegistrar registrar1(partition, true);
880	PartitionRegistrar registrar2(partition->Device(), true);
881	DeviceWriteLocker locker(partition->Device(), true);
882
883	// check change counter
884	if (changeCounter != partition->ChangeCounter())
885		return B_BAD_VALUE;
886
887	// the partition must be initialized
888	KDiskSystem* diskSystem = partition->DiskSystem();
889	if (!diskSystem)
890		return B_BAD_VALUE;
891
892	// mark the partition busy and unlock
893	if (!partition->CheckAndMarkBusy(false))
894		return B_BUSY;
895	locker.Unlock();
896
897	// set content parameters
898	error = diskSystem->SetContentName(partition, name.value, DUMMY_JOB_ID);
899
900	// re-lock and unmark busy
901	locker.Lock();
902	partition->UnmarkBusy(false);
903
904	if (error != B_OK)
905		return error;
906
907	// return change counter
908	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
909			!= B_OK) {
910		return error;
911	}
912
913	return B_OK;
914}
915
916
917status_t
918_user_set_partition_type(partition_id partitionID, int32* _changeCounter,
919	partition_id childID, int32* _childChangeCounter, const char* _type)
920{
921	// copy parameters in
922	UserStringParameter<false> type;
923	int32 changeCounter;
924	int32 childChangeCounter;
925
926	status_t error;
927	if ((error = type.Init(_type, B_DISK_DEVICE_TYPE_LENGTH)) != B_OK
928		|| (error = copy_from_user_value(changeCounter, _changeCounter))
929			!= B_OK
930		|| (error = copy_from_user_value(childChangeCounter,
931			_childChangeCounter)) != B_OK) {
932		return error;
933	}
934
935	// get the partition
936	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
937	KPartition* partition = manager->WriteLockPartition(partitionID);
938	if (!partition)
939		return B_ENTRY_NOT_FOUND;
940
941	PartitionRegistrar registrar1(partition, true);
942	PartitionRegistrar registrar2(partition->Device(), true);
943	DeviceWriteLocker locker(partition->Device(), true);
944
945	// register child
946	KPartition* child = manager->RegisterPartition(childID);
947	if (!child)
948		return B_ENTRY_NOT_FOUND;
949
950	PartitionRegistrar registrar3(child, true);
951
952	// check change counters
953	if (changeCounter != partition->ChangeCounter()
954		|| childChangeCounter != child->ChangeCounter()) {
955		return B_BAD_VALUE;
956	}
957
958	// the partition must be initialized
959	KDiskSystem* diskSystem = partition->DiskSystem();
960	if (!diskSystem)
961		return B_BAD_VALUE;
962
963	// child must indeed be a child of partition
964	if (child->Parent() != partition)
965		return B_BAD_VALUE;
966
967	// mark the partition busy and unlock
968	if (partition->IsBusy() || child->IsBusy())
969		return B_BUSY;
970	partition->SetBusy(true);
971	child->SetBusy(true);
972	locker.Unlock();
973
974	// set the child type
975	error = diskSystem->SetType(child, type.value, DUMMY_JOB_ID);
976
977	// re-lock and unmark busy
978	locker.Lock();
979	partition->SetBusy(false);
980	child->SetBusy(false);
981
982	if (error != B_OK)
983		return error;
984
985	// return change counters
986	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
987			!= B_OK
988		|| (error = copy_to_user_value(_childChangeCounter,
989			child->ChangeCounter())) != B_OK) {
990		return error;
991	}
992
993	return B_OK;
994}
995
996
997status_t
998_user_set_partition_parameters(partition_id partitionID, int32* _changeCounter,
999	partition_id childID, int32* _childChangeCounter, const char* _parameters)
1000{
1001	// copy parameters in
1002	UserStringParameter<true> parameters;
1003	int32 changeCounter;
1004	int32 childChangeCounter;
1005
1006	status_t error;
1007	if ((error = parameters.Init(_parameters, B_DISK_DEVICE_MAX_PARAMETER_SIZE))
1008			!= B_OK
1009		|| (error = copy_from_user_value(changeCounter, _changeCounter))
1010			!= B_OK
1011		|| (error = copy_from_user_value(childChangeCounter,
1012			_childChangeCounter)) != B_OK) {
1013		return error;
1014	}
1015
1016	// get the partition
1017	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1018	KPartition* partition = manager->WriteLockPartition(partitionID);
1019	if (!partition)
1020		return B_ENTRY_NOT_FOUND;
1021
1022	PartitionRegistrar registrar1(partition, true);
1023	PartitionRegistrar registrar2(partition->Device(), true);
1024	DeviceWriteLocker locker(partition->Device(), true);
1025
1026	// register child
1027	KPartition* child = manager->RegisterPartition(childID);
1028	if (!child)
1029		return B_ENTRY_NOT_FOUND;
1030
1031	PartitionRegistrar registrar3(child, true);
1032
1033	// check change counters
1034	if (changeCounter != partition->ChangeCounter()
1035		|| childChangeCounter != child->ChangeCounter()) {
1036		return B_BAD_VALUE;
1037	}
1038
1039	// the partition must be initialized
1040	KDiskSystem* diskSystem = partition->DiskSystem();
1041	if (!diskSystem)
1042		return B_BAD_VALUE;
1043
1044	// child must indeed be a child of partition
1045	if (child->Parent() != partition)
1046		return B_BAD_VALUE;
1047
1048	// mark the partition busy and unlock
1049	if (partition->IsBusy() || child->IsBusy())
1050		return B_BUSY;
1051	partition->SetBusy(true);
1052	child->SetBusy(true);
1053	locker.Unlock();
1054
1055	// set the child parameters
1056	error = diskSystem->SetParameters(child, parameters.value, DUMMY_JOB_ID);
1057
1058	// re-lock and unmark busy
1059	locker.Lock();
1060	partition->SetBusy(false);
1061	child->SetBusy(false);
1062
1063	if (error != B_OK)
1064		return error;
1065
1066	// return change counters
1067	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
1068			!= B_OK
1069		|| (error = copy_to_user_value(_childChangeCounter,
1070			child->ChangeCounter())) != B_OK) {
1071		return error;
1072	}
1073
1074	return B_OK;
1075}
1076
1077
1078status_t
1079_user_set_partition_content_parameters(partition_id partitionID,
1080	int32* _changeCounter, const char* _parameters)
1081{
1082	// copy parameters in
1083	UserStringParameter<true> parameters;
1084	int32 changeCounter;
1085
1086	status_t error
1087		= parameters.Init(_parameters, B_DISK_DEVICE_MAX_PARAMETER_SIZE);
1088	if (error != B_OK || (error = copy_from_user_value(changeCounter,
1089			_changeCounter)) != B_OK) {
1090		return error;
1091	}
1092
1093	// get the partition
1094	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1095	KPartition* partition = manager->WriteLockPartition(partitionID);
1096	if (!partition)
1097		return B_ENTRY_NOT_FOUND;
1098
1099	PartitionRegistrar registrar1(partition, true);
1100	PartitionRegistrar registrar2(partition->Device(), true);
1101	DeviceWriteLocker locker(partition->Device(), true);
1102
1103	// check change counter
1104	if (changeCounter != partition->ChangeCounter())
1105		return B_BAD_VALUE;
1106
1107	// the partition must be initialized
1108	KDiskSystem* diskSystem = partition->DiskSystem();
1109	if (!diskSystem)
1110		return B_BAD_VALUE;
1111
1112	// mark the partition busy and unlock
1113	if (!partition->CheckAndMarkBusy(true))
1114		return B_BUSY;
1115	locker.Unlock();
1116
1117	// set content parameters
1118	error = diskSystem->SetContentParameters(partition, parameters.value,
1119		DUMMY_JOB_ID);
1120
1121	// re-lock and unmark busy
1122	locker.Lock();
1123	partition->UnmarkBusy(true);
1124
1125	if (error != B_OK)
1126		return error;
1127
1128	// return change counter
1129	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
1130			!= B_OK) {
1131		return error;
1132	}
1133
1134	return B_OK;
1135}
1136
1137
1138status_t
1139_user_initialize_partition(partition_id partitionID, int32* _changeCounter,
1140	const char* _diskSystemName, const char* _name, const char* _parameters)
1141{
1142	// copy parameters in
1143	UserStringParameter<false> diskSystemName;
1144	UserStringParameter<true> name;
1145	UserStringParameter<true> parameters;
1146	int32 changeCounter;
1147
1148	status_t error;
1149	if ((error = diskSystemName.Init(_diskSystemName,
1150			B_DISK_SYSTEM_NAME_LENGTH)) != B_OK
1151		|| (error = name.Init(_name, B_DISK_DEVICE_NAME_LENGTH)) != B_OK
1152		|| (error = parameters.Init(_parameters,
1153				B_DISK_DEVICE_MAX_PARAMETER_SIZE)) != B_OK
1154		|| (error = copy_from_user_value(changeCounter, _changeCounter))
1155				!= B_OK) {
1156		return error;
1157	}
1158
1159	// get the partition
1160	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1161	KPartition* partition = manager->WriteLockPartition(partitionID);
1162	if (!partition)
1163		return B_ENTRY_NOT_FOUND;
1164
1165	PartitionRegistrar registrar1(partition, true);
1166	PartitionRegistrar registrar2(partition->Device(), true);
1167	DeviceWriteLocker locker(partition->Device(), true);
1168
1169	// check change counter
1170	if (changeCounter != partition->ChangeCounter())
1171		return B_BAD_VALUE;
1172
1173	// the partition must be uninitialized
1174	if (partition->DiskSystem())
1175		return B_BAD_VALUE;
1176
1177	// load the new disk system
1178	KDiskSystem *diskSystem = manager->LoadDiskSystem(diskSystemName.value,
1179		true);
1180	if (!diskSystem)
1181		return B_ENTRY_NOT_FOUND;
1182	DiskSystemLoader loader(diskSystem, true);
1183
1184	// mark the partition busy and unlock
1185	if (!partition->CheckAndMarkBusy(true))
1186		return B_BUSY;
1187	locker.Unlock();
1188
1189	// let the disk system initialize the partition
1190	error = diskSystem->Initialize(partition, name.value, parameters.value,
1191		DUMMY_JOB_ID);
1192
1193	// re-lock and unmark busy
1194	locker.Lock();
1195	partition->UnmarkBusy(true);
1196
1197	if (error != B_OK)
1198		return error;
1199
1200	// Set the disk system. Re-check whether a disk system is already set on the
1201	// partition. Some disk systems just write the on-disk structures and let
1202	// the DDM rescan the partition, in which case the disk system will already
1203	// be set. In very unfortunate cases the on-disk structure of the previous
1204	// disk system has not been destroyed and the previous disk system has a
1205	// higher priority than the new one. The old disk system will thus prevail.
1206	// Not setting the new disk system will at least prevent that the partition
1207	// object gets into an inconsistent state.
1208	if (partition->DiskSystem() == NULL)
1209		partition->SetDiskSystem(diskSystem);
1210
1211	// return change counter
1212	error = copy_to_user_value(_changeCounter, partition->ChangeCounter());
1213	if (error != B_OK)
1214		return error;
1215
1216	return B_OK;
1217}
1218
1219
1220status_t
1221_user_uninitialize_partition(partition_id partitionID, int32* _changeCounter)
1222{
1223	// copy parameters in
1224	int32 changeCounter;
1225
1226	status_t error;
1227	if ((error = copy_from_user_value(changeCounter, _changeCounter)) != B_OK)
1228		return error;
1229
1230	// get the partition
1231	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1232	KPartition* partition = manager->WriteLockPartition(partitionID);
1233	if (!partition)
1234		return B_ENTRY_NOT_FOUND;
1235
1236	PartitionRegistrar registrar1(partition, true);
1237	PartitionRegistrar registrar2(partition->Device(), true);
1238	DeviceWriteLocker locker(partition->Device(), true);
1239
1240	// check change counter
1241	if (changeCounter != partition->ChangeCounter())
1242		return B_BAD_VALUE;
1243
1244	// the partition must be initialized
1245	if (!partition->DiskSystem())
1246		return B_BAD_VALUE;
1247
1248	// check busy
1249	if (!partition->CheckAndMarkBusy(true))
1250		return B_BUSY;
1251
1252	// TODO: We should also check, if any partition is mounted!
1253
1254	KDiskSystem* diskSystem = partition->DiskSystem();
1255
1256	locker.Unlock();
1257
1258	// Let the disk system uninitialize the partition. This operation is not
1259	// mandatory. If implemented, it will destroy the on-disk structures, so
1260	// that the disk system cannot accidentally identify the partition later on.
1261	if (diskSystem != NULL)
1262		diskSystem->Uninitialize(partition, DUMMY_JOB_ID);
1263
1264	// re-lock and uninitialize the partition object
1265	locker.Lock();
1266	error = partition->UninitializeContents(true);
1267
1268	partition->UnmarkBusy(true);
1269
1270	if (error != B_OK)
1271		return error;
1272
1273	// return change counter
1274	error = copy_to_user_value(_changeCounter, partition->ChangeCounter());
1275	if (error != B_OK)
1276		return error;
1277
1278	return B_OK;
1279}
1280
1281
1282status_t
1283_user_create_child_partition(partition_id partitionID, int32* _changeCounter,
1284	off_t offset, off_t size, const char* _type, const char* _name,
1285	const char* _parameters, partition_id* childID, int32* childChangeCounter)
1286{
1287	// copy parameters in
1288	UserStringParameter<false> type;
1289	UserStringParameter<true> name;
1290	UserStringParameter<true> parameters;
1291	int32 changeCounter;
1292
1293	status_t error;
1294	if ((error = type.Init(_type, B_DISK_DEVICE_TYPE_LENGTH)) != B_OK
1295		|| (error = name.Init(_name, B_DISK_DEVICE_NAME_LENGTH)) != B_OK
1296		|| (error = parameters.Init(_parameters,
1297				B_DISK_DEVICE_MAX_PARAMETER_SIZE)) != B_OK
1298		|| (error = copy_from_user_value(changeCounter, _changeCounter))
1299				!= B_OK) {
1300		return error;
1301	}
1302
1303	// get the partition
1304	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1305	KPartition* partition = manager->WriteLockPartition(partitionID);
1306	if (!partition)
1307		return B_ENTRY_NOT_FOUND;
1308
1309	PartitionRegistrar registrar1(partition, true);
1310	PartitionRegistrar registrar2(partition->Device(), true);
1311	DeviceWriteLocker locker(partition->Device(), true);
1312
1313	// check change counter
1314	if (changeCounter != partition->ChangeCounter())
1315		return B_BAD_VALUE;
1316
1317	// the partition must be initialized
1318	KDiskSystem* diskSystem = partition->DiskSystem();
1319	if (!diskSystem)
1320		return B_BAD_VALUE;
1321
1322	// mark the partition busy and unlock
1323	if (!partition->CheckAndMarkBusy(false))
1324		return B_BUSY;
1325	locker.Unlock();
1326
1327	// create the child
1328	KPartition *child = NULL;
1329	error = diskSystem->CreateChild(partition, offset, size, type.value,
1330		name.value, parameters.value, DUMMY_JOB_ID, &child, -1);
1331
1332	// re-lock and unmark busy
1333	locker.Lock();
1334	partition->UnmarkBusy(false);
1335
1336	if (error != B_OK)
1337		return error;
1338
1339	if (child == NULL)
1340		return B_ERROR;
1341
1342	child->UnmarkBusy(true);
1343
1344	// return change counter and child ID
1345	if ((error = copy_to_user_value(_changeCounter, partition->ChangeCounter()))
1346			!= B_OK
1347		|| (error = copy_to_user_value(childID, child->ID())) != B_OK) {
1348		return error;
1349	}
1350
1351	return B_OK;
1352}
1353
1354
1355status_t
1356_user_delete_child_partition(partition_id partitionID, int32* _changeCounter,
1357	partition_id childID, int32 childChangeCounter)
1358{
1359	// copy parameters in
1360	int32 changeCounter;
1361
1362	status_t error;
1363	if ((error = copy_from_user_value(changeCounter, _changeCounter)) != B_OK)
1364		return error;
1365
1366	// get the partition
1367	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1368	KPartition* partition = manager->WriteLockPartition(partitionID);
1369	if (!partition)
1370		return B_ENTRY_NOT_FOUND;
1371
1372	PartitionRegistrar registrar1(partition, true);
1373	PartitionRegistrar registrar2(partition->Device(), true);
1374	DeviceWriteLocker locker(partition->Device(), true);
1375
1376	// register child
1377	KPartition* child = manager->RegisterPartition(childID);
1378	if (!child)
1379		return B_ENTRY_NOT_FOUND;
1380
1381	PartitionRegistrar registrar3(child, true);
1382
1383	// check change counters
1384	if (changeCounter != partition->ChangeCounter()
1385		|| childChangeCounter != child->ChangeCounter()) {
1386		return B_BAD_VALUE;
1387	}
1388
1389	// the partition must be initialized
1390	KDiskSystem* diskSystem = partition->DiskSystem();
1391	if (!diskSystem)
1392		return B_BAD_VALUE;
1393
1394	// child must indeed be a child of partition
1395	if (child->Parent() != partition)
1396		return B_BAD_VALUE;
1397
1398	// mark the partition and child busy and unlock
1399	if (partition->IsBusy() || !child->CheckAndMarkBusy(true))
1400		return B_BUSY;
1401	partition->SetBusy(true);
1402	locker.Unlock();
1403
1404	// delete the child
1405	error = diskSystem->DeleteChild(child, DUMMY_JOB_ID);
1406
1407	// re-lock and unmark busy
1408	locker.Lock();
1409	partition->SetBusy(false);
1410	child->UnmarkBusy(true);
1411
1412	if (error != B_OK)
1413		return error;
1414
1415	// return change counter
1416	return copy_to_user_value(_changeCounter, partition->ChangeCounter());
1417}
1418
1419
1420status_t
1421_user_start_watching_disks(uint32 eventMask, port_id port, int32 token)
1422{
1423	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1424	return manager->Notifications().UpdateUserListener(eventMask, port, token);
1425}
1426
1427
1428status_t
1429_user_stop_watching_disks(port_id port, int32 token)
1430{
1431	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1432	return manager->Notifications().RemoveUserListeners(port, token);
1433}
1434
1435