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