xref: /haiku/src/servers/mount/AutoMounter.cpp (revision fccd8899fcb583bfb73c5c26c9fcd714b963959b)
1 /*
2  * Copyright 2007-2010, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus, superstippi@gmx.de
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 #include "AutoMounter.h"
12 
13 #include <new>
14 
15 #include <string.h>
16 #include <unistd.h>
17 
18 #include <Alert.h>
19 #include <AutoLocker.h>
20 #include <Catalog.h>
21 #include <Debug.h>
22 #include <Directory.h>
23 #include <DiskDevice.h>
24 #include <DiskDeviceRoster.h>
25 #include <DiskDeviceList.h>
26 #include <DiskDeviceTypes.h>
27 #include <DiskSystem.h>
28 #include <FindDirectory.h>
29 #include <fs_info.h>
30 #include <fs_volume.h>
31 #include <Locale.h>
32 #include <Message.h>
33 #include <Node.h>
34 #include <NodeMonitor.h>
35 #include <Path.h>
36 #include <PropertyInfo.h>
37 #include <String.h>
38 #include <VolumeRoster.h>
39 
40 //#include "AutoMounterSettings.h"
41 #include "MountServer.h"
42 
43 
44 #undef B_TRANSLATE_CONTEXT
45 #define B_TRANSLATE_CONTEXT "AutoMounter"
46 
47 
48 static const char* kMountServerSettings = "mount_server";
49 static const uint32 kMsgInitialScan = 'insc';
50 static const char* kMountFlagsKeyExtension = " mount flags";
51 
52 
53 bool
54 BootedInSafeMode()
55 {
56 	const char *safeMode = getenv("SAFEMODE");
57 	return (safeMode && strcmp(safeMode, "yes") == 0);
58 }
59 
60 
61 // #pragma mark -
62 
63 
64 AutoMounter::AutoMounter()
65 	:
66 	BApplication(kMountServerSignature),
67 	fNormalMode(kRestorePreviousVolumes),
68 	fRemovableMode(kAllVolumes),
69 	fEjectWhenUnmounting(true)
70 {
71 	set_thread_priority(Thread(), B_LOW_PRIORITY);
72 
73 	if (!BootedInSafeMode()) {
74 		_ReadSettings();
75 	} else {
76 		// defeat automounter in safe mode, don't even care about the settings
77 		fNormalMode = kNoVolumes;
78 		fRemovableMode = kNoVolumes;
79 	}
80 
81 	BDiskDeviceRoster().StartWatching(this,
82 		B_DEVICE_REQUEST_DEVICE | B_DEVICE_REQUEST_DEVICE_LIST);
83 }
84 
85 
86 AutoMounter::~AutoMounter()
87 {
88 	BDiskDeviceRoster().StopWatching(this);
89 }
90 
91 
92 void
93 AutoMounter::MessageReceived(BMessage* message)
94 {
95 	switch (message->what) {
96 		case B_EXECUTE_PROPERTY:
97 		{
98 			int32 index;
99 			BMessage specifier;
100 			int32 what;
101 			const char* property = NULL;
102 			if (message->GetCurrentSpecifier(&index, &specifier, &what,
103 					&property) < B_OK
104 				|| !_ScriptReceived(message, index, &specifier, what,
105 					property)) {
106 				BApplication::MessageReceived(message);
107 			}
108 			break;
109 		}
110 
111 		case kMsgInitialScan:
112 			_MountVolumes(fNormalMode, fRemovableMode, true);
113 			break;
114 
115 		case kMountVolume:
116 			_MountVolume(message);
117 			break;
118 
119 		case kUnmountVolume:
120 			_UnmountAndEjectVolume(message);
121 			break;
122 
123 		case kSetAutomounterParams:
124 		{
125 			bool rescanNow = false;
126 			message->FindBool("rescanNow", &rescanNow);
127 
128 			_UpdateSettingsFromMessage(message);
129 			_GetSettings(&fSettings);
130 			_WriteSettings();
131 
132 			if (rescanNow)
133 				_MountVolumes(fNormalMode, fRemovableMode);
134 			break;
135 		}
136 
137 		case kGetAutomounterParams:
138 		{
139 			BMessage reply;
140 			_GetSettings(&reply);
141 			message->SendReply(&reply);
142 			break;
143 		}
144 
145 		case kMountAllNow:
146 			_MountVolumes(kAllVolumes, kAllVolumes);
147 			break;
148 
149 		case B_DEVICE_UPDATE:
150 			int32 event;
151 			if (message->FindInt32("event", &event) != B_OK
152 				|| (event != B_DEVICE_MEDIA_CHANGED
153 					&& event != B_DEVICE_ADDED))
154 				break;
155 
156 			partition_id deviceID;
157 			if (message->FindInt32("id", &deviceID) != B_OK)
158 				break;
159 
160 			_MountVolumes(kNoVolumes, fRemovableMode, false, deviceID);
161 			break;
162 
163 #if 0
164 		case B_NODE_MONITOR:
165 		{
166 			int32 opcode;
167 			if (message->FindInt32("opcode", &opcode) != B_OK)
168 				break;
169 
170 			switch (opcode) {
171 				//	The name of a mount point has changed
172 				case B_ENTRY_MOVED: {
173 					WRITELOG(("*** Received Mount Point Renamed Notification"));
174 
175 					const char *newName;
176 					if (message->FindString("name", &newName) != B_OK) {
177 						WRITELOG(("ERROR: Couldn't find name field in update "
178 							"message"));
179 						PRINT_OBJECT(*message);
180 						break ;
181 					}
182 
183 					//
184 					// When the node monitor reports a move, it gives the
185 					// parent device and inode that moved.  The problem is
186 					// that  the inode is the inode of root *in* the filesystem,
187 					// which is generally always the same number for every
188 					// filesystem of a type.
189 					//
190 					// What we'd really like is the device that the moved
191 					// volume is mounted on.  Find this by using the
192 					// *new* name and directory, and then stat()ing that to
193 					// find the device.
194 					//
195 					dev_t parentDevice;
196 					if (message->FindInt32("device", &parentDevice) != B_OK) {
197 						WRITELOG(("ERROR: Couldn't find 'device' field in "
198 							"update message"));
199 						PRINT_OBJECT(*message);
200 						break;
201 					}
202 
203 					ino_t toDirectory;
204 					if (message->FindInt64("to directory", &toDirectory)
205 						!= B_OK) {
206 						WRITELOG(("ERROR: Couldn't find 'to directory' field "
207 							"in update message"));
208 						PRINT_OBJECT(*message);
209 						break;
210 					}
211 
212 					entry_ref root_entry(parentDevice, toDirectory, newName);
213 
214 					BNode entryNode(&root_entry);
215 					if (entryNode.InitCheck() != B_OK) {
216 						WRITELOG(("ERROR: Couldn't create mount point entry "
217 							"node: %s/n", strerror(entryNode.InitCheck())));
218 						break;
219 					}
220 
221 					node_ref mountPointNode;
222 					if (entryNode.GetNodeRef(&mountPointNode) != B_OK) {
223 						WRITELOG(("ERROR: Couldn't get node ref for new mount "
224 							"point"));
225 						break;
226 					}
227 
228 					WRITELOG(("Attempt to rename device %li to %s",
229 						mountPointNode.device, newName));
230 
231 					Partition *partition = FindPartition(mountPointNode.device);
232 					if (partition != NULL) {
233 						WRITELOG(("Found device, changing name."));
234 
235 						BVolume mountVolume(partition->VolumeDeviceID());
236 						BDirectory mountDir;
237 						mountVolume.GetRootDirectory(&mountDir);
238 						BPath dirPath(&mountDir, 0);
239 
240 						partition->SetMountedAt(dirPath.Path());
241 						partition->SetVolumeName(newName);
242 						break;
243 					} else {
244 						WRITELOG(("ERROR: Device %li does not appear to be "
245 							"present", mountPointNode.device));
246 					}
247 				}
248 			}
249 			break;
250 		}
251 #endif
252 
253 		default:
254 			BLooper::MessageReceived(message);
255 			break;
256 	}
257 }
258 
259 
260 bool
261 AutoMounter::QuitRequested()
262 {
263 	if (!BootedInSafeMode()) {
264 		// Don't write out settings in safe mode - this would overwrite the
265 		// normal, non-safe mode settings.
266 		_WriteSettings();
267 	}
268 
269 	return true;
270 }
271 
272 
273 // #pragma mark - scripting
274 
275 
276 const uint32 kApplication = 0;
277 
278 static property_info sPropertyInfo[] = {
279 	{
280 		"InitialScan",
281 		{B_EXECUTE_PROPERTY},
282 		{B_DIRECT_SPECIFIER},
283 		NULL, kApplication,
284 		{B_STRING_TYPE},
285 		{},
286 		{}
287 	},
288 	{}
289 };
290 
291 
292 BHandler*
293 AutoMounter::ResolveSpecifier(BMessage* message, int32 index,
294 	BMessage* specifier, int32 what, const char* property)
295 {
296 	BPropertyInfo propInfo(sPropertyInfo);
297 
298 	uint32 data;
299 	if (propInfo.FindMatch(message, 0, specifier, what, property, &data) >= 0) {
300 		if (data == kApplication)
301 			return this;
302 
303 		BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
304 		reply.AddInt32("error", B_ERROR);
305 		reply.AddString("message", "Unkown specifier.");
306 		message->SendReply(&reply);
307 		return NULL;
308 	}
309 
310 	return BApplication::ResolveSpecifier(message, index, specifier, what,
311 		property);
312 }
313 
314 
315 status_t
316 AutoMounter::GetSupportedSuites(BMessage* data)
317 {
318 	if (data == NULL)
319 		return B_BAD_VALUE;
320 
321 	status_t status = data->AddString("suites",
322 		"suite/vnd.Haiku-mount_server");
323 	if (status != B_OK)
324 		return status;
325 
326 	BPropertyInfo propertyInfo(sPropertyInfo);
327 	status = data->AddFlat("messages", &propertyInfo);
328 	if (status != B_OK)
329 		return status;
330 
331 	return BApplication::GetSupportedSuites(data);
332 }
333 
334 
335 bool
336 AutoMounter::_ScriptReceived(BMessage *message, int32 index,
337 	BMessage *specifier, int32 what, const char *property)
338 {
339 	BMessage reply(B_REPLY);
340 	status_t err = B_BAD_SCRIPT_SYNTAX;
341 
342 	switch (message->what) {
343 		case B_EXECUTE_PROPERTY:
344 			if (strcmp("InitialScan", property) == 0) {
345 				_MountVolumes(fNormalMode, fRemovableMode, true);
346 				err = reply.AddString("result",
347 					B_TRANSLATE("Previous volumes mounted."));
348 			}
349 			break;
350 	}
351 
352 	if (err == B_BAD_SCRIPT_SYNTAX)
353 		return false;
354 
355 	if (err != B_OK) {
356 		reply.what = B_MESSAGE_NOT_UNDERSTOOD;
357 		reply.AddString("message", strerror(err));
358 	}
359 	reply.AddInt32("error", err);
360 	message->SendReply(&reply);
361 	return true;
362 }
363 
364 
365 // #pragma mark -
366 
367 
368 static bool
369 suggest_mount_flags(const BPartition* partition, uint32* _flags)
370 {
371 	uint32 mountFlags = 0;
372 
373 	bool askReadOnly = true;
374 	bool isBFS = false;
375 
376 	if (partition->ContentType() != NULL
377 		&& strcmp(partition->ContentType(), kPartitionTypeBFS) == 0) {
378 #if 0
379 		askReadOnly = false;
380 #endif
381 		isBFS = true;
382 	}
383 
384 	BDiskSystem diskSystem;
385 	status_t status = partition->GetDiskSystem(&diskSystem);
386 	if (status == B_OK && !diskSystem.SupportsWriting())
387 		askReadOnly = false;
388 
389 	if (partition->IsReadOnly())
390 		askReadOnly = false;
391 
392 	if (askReadOnly) {
393 		// Suggest to the user to mount read-only until Haiku is more mature.
394 		BString string;
395 		if (partition->ContentName() != NULL) {
396 			char buffer[512];
397 			snprintf(buffer, sizeof(buffer),
398 				B_TRANSLATE("Mounting volume '%s'\n\n"),
399 				partition->ContentName());
400 			string << buffer;
401 		} else
402 			string << B_TRANSLATE("Mounting volume <unnamed volume>\n\n");
403 
404 		// TODO: Use distro name instead of "Haiku"...
405 		if (!isBFS) {
406 			string << B_TRANSLATE("The file system on this volume is not the "
407 				"Haiku file system. It is strongly suggested to mount it in "
408 				"read-only mode. This will prevent unintentional data loss "
409 				"because of errors in Haiku.");
410 		} else {
411 			string << B_TRANSLATE("It is suggested to mount all additional "
412 				"Haiku volumes in read-only mode. This will prevent "
413 				"unintentional data loss because of errors in Haiku.");
414 		}
415 
416 		BAlert* alert = new BAlert(B_TRANSLATE("Mount warning"),
417 			string.String(), B_TRANSLATE("Mount read/write"),
418 			B_TRANSLATE("Cancel"), B_TRANSLATE("Mount read-only"),
419 			B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
420 		alert->SetShortcut(1, B_ESCAPE);
421 		int32 choice = alert->Go();
422 		switch (choice) {
423 			case 0:
424 				break;
425 			case 1:
426 				return false;
427 			case 2:
428 				mountFlags |= B_MOUNT_READ_ONLY;
429 				break;
430 		}
431 	}
432 
433 	*_flags = mountFlags;
434 	return true;
435 }
436 
437 
438 void
439 AutoMounter::_MountVolumes(mount_mode normal, mount_mode removable,
440 	bool initialRescan, partition_id deviceID)
441 {
442 	if (normal == kNoVolumes && removable == kNoVolumes)
443 		return;
444 
445 	class InitialMountVisitor : public BDiskDeviceVisitor {
446 		public:
447 			InitialMountVisitor(mount_mode normalMode, mount_mode removableMode,
448 					bool initialRescan, BMessage& previous,
449 					partition_id deviceID)
450 				:
451 				fNormalMode(normalMode),
452 				fRemovableMode(removableMode),
453 				fInitialRescan(initialRescan),
454 				fPrevious(previous),
455 				fOnlyOnDeviceID(deviceID)
456 			{
457 			}
458 
459 			virtual
460 			~InitialMountVisitor()
461 			{
462 			}
463 
464 			virtual bool
465 			Visit(BDiskDevice* device)
466 			{
467 				return Visit(device, 0);
468 			}
469 
470 			virtual bool
471 			Visit(BPartition* partition, int32 level)
472 			{
473 				if (fOnlyOnDeviceID >= 0) {
474 					// only mount partitions on the given device id
475 					// or if the partition ID is already matched
476 					BPartition* device = partition;
477 					while (device->Parent() != NULL) {
478 						if (device->ID() == fOnlyOnDeviceID) {
479 							// we are happy
480 							break;
481 						}
482 						device = device->Parent();
483 					}
484 					if (device->ID() != fOnlyOnDeviceID)
485 						return false;
486 				}
487 
488 				mount_mode mode = !fInitialRescan
489 					&& partition->Device()->IsRemovableMedia()
490 					? fRemovableMode : fNormalMode;
491 				if (mode == kNoVolumes
492 					|| partition->IsMounted()
493 					|| !partition->ContainsFileSystem())
494 					return false;
495 
496 				BPath path;
497 				if (partition->GetPath(&path) != B_OK)
498 					return false;
499 
500 				if (mode == kRestorePreviousVolumes) {
501 					// mount all volumes that were stored in the settings file
502 					const char *volumeName = NULL;
503 					if (partition->ContentName() == NULL
504 						|| fPrevious.FindString(path.Path(), &volumeName)
505 							!= B_OK
506 						|| strcmp(volumeName, partition->ContentName()))
507 						return false;
508 				} else if (mode == kOnlyBFSVolumes) {
509 					if (partition->ContentType() == NULL
510 						|| strcmp(partition->ContentType(), kPartitionTypeBFS))
511 						return false;
512 				}
513 
514 				uint32 mountFlags;
515 				if (!fInitialRescan) {
516 					// Ask the user about mount flags if this is not the
517 					// initial scan.
518 					if (!suggest_mount_flags(partition, &mountFlags))
519 						return false;
520 				} else {
521 					BString mountFlagsKey(path.Path());
522 					mountFlagsKey << kMountFlagsKeyExtension;
523 					if (fPrevious.FindInt32(mountFlagsKey.String(),
524 							(int32*)&mountFlags) < B_OK) {
525 						mountFlags = 0;
526 					}
527 				}
528 
529 				if (partition->Mount(NULL, mountFlags) != B_OK) {
530 					// TODO: Error to syslog
531 				}
532 				return false;
533 			}
534 
535 		private:
536 			mount_mode		fNormalMode;
537 			mount_mode		fRemovableMode;
538 			bool			fInitialRescan;
539 			BMessage&		fPrevious;
540 			partition_id	fOnlyOnDeviceID;
541 	} visitor(normal, removable, initialRescan, fSettings, deviceID);
542 
543 	BDiskDeviceList devices;
544 	status_t status = devices.Fetch();
545 	if (status == B_OK)
546 		devices.VisitEachPartition(&visitor);
547 }
548 
549 
550 void
551 AutoMounter::_MountVolume(const BMessage* message)
552 {
553 	int32 id;
554 	if (message->FindInt32("id", &id) != B_OK)
555 		return;
556 
557 	BDiskDeviceRoster roster;
558 	BPartition *partition;
559 	BDiskDevice device;
560 	if (roster.GetPartitionWithID(id, &device, &partition) != B_OK)
561 		return;
562 
563 	uint32 mountFlags;
564 	if (!suggest_mount_flags(partition, &mountFlags))
565 		return;
566 
567 	status_t status = partition->Mount(NULL, mountFlags);
568 	if (status < B_OK) {
569 		char text[512];
570 		snprintf(text, sizeof(text),
571 			B_TRANSLATE("Error mounting volume:\n\n%s"), strerror(status));
572 		(new BAlert(B_TRANSLATE("Mount error"), text,
573 			B_TRANSLATE("OK")))->Go(NULL);
574 	}
575 }
576 
577 
578 bool
579 AutoMounter::_SuggestForceUnmount(const char* name, status_t error)
580 {
581 	char text[1024];
582 	snprintf(text, sizeof(text),
583 		B_TRANSLATE("Could not unmount disk \"%s\":\n\t%s\n\n"
584 			"Should unmounting be forced?\n\n"
585 			"Note: If an application is currently writing to the volume, "
586 			"unmounting it now might result in loss of data.\n"),
587 		name, strerror(error));
588 
589 	BAlert* alert = new BAlert(B_TRANSLATE("Force unmount"), text,
590 		B_TRANSLATE("Cancel"), B_TRANSLATE("Force unmount"), NULL,
591 		B_WIDTH_AS_USUAL, B_WARNING_ALERT);
592 	alert->SetShortcut(0, B_ESCAPE);
593 	int32 choice = alert->Go();
594 
595 	return choice == 1;
596 }
597 
598 
599 void
600 AutoMounter::_ReportUnmountError(const char* name, status_t error)
601 {
602 	char text[512];
603 	snprintf(text, sizeof(text), B_TRANSLATE("Could not unmount disk "
604 		"\"%s\":\n\t%s"), name, strerror(error));
605 
606 	(new BAlert(B_TRANSLATE("Unmount error"), text, B_TRANSLATE("OK"),
607 		NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL);
608 }
609 
610 
611 void
612 AutoMounter::_UnmountAndEjectVolume(BPartition* partition, BPath& mountPoint,
613 	const char* name)
614 {
615 	BDiskDevice device;
616 	if (partition == NULL) {
617 		// Try to retrieve partition
618 		BDiskDeviceRoster().FindPartitionByMountPoint(mountPoint.Path(),
619 			&device, &partition);
620 	}
621 
622 	status_t status;
623 	if (partition != NULL)
624 		status = partition->Unmount();
625 	else
626 		status = fs_unmount_volume(mountPoint.Path(), 0);
627 
628 	if (status != B_OK) {
629 		if (!_SuggestForceUnmount(name, status))
630 			return;
631 
632 		if (partition != NULL)
633 			status = partition->Unmount(B_FORCE_UNMOUNT);
634 		else
635 			status = fs_unmount_volume(mountPoint.Path(), B_FORCE_UNMOUNT);
636 	}
637 
638 	if (status != B_OK) {
639 		_ReportUnmountError(name, status);
640 		return;
641 	}
642 
643 	if (fEjectWhenUnmounting && partition != NULL) {
644 		// eject device if it doesn't have any mounted partitions left
645 		class IsMountedVisitor : public BDiskDeviceVisitor {
646 		public:
647 			IsMountedVisitor()
648 				:
649 				fHasMounted(false)
650 			{
651 			}
652 
653 			virtual bool Visit(BDiskDevice* device)
654 			{
655 				return Visit(device, 0);
656 			}
657 
658 			virtual bool Visit(BPartition* partition, int32 level)
659 			{
660 				if (partition->IsMounted()) {
661 					fHasMounted = true;
662 					return true;
663 				}
664 
665 				return false;
666 			}
667 
668 			bool HasMountedPartitions() const
669 			{
670 				return fHasMounted;
671 			}
672 
673 		private:
674 			bool	fHasMounted;
675 		} visitor;
676 
677 		partition->Device()->VisitEachDescendant(&visitor);
678 
679 		if (!visitor.HasMountedPartitions())
680 			partition->Device()->Eject();
681 	}
682 
683 	// remove the directory if it's a directory in rootfs
684 	if (dev_for_path(mountPoint.Path()) == dev_for_path("/"))
685 		rmdir(mountPoint.Path());
686 }
687 
688 
689 void
690 AutoMounter::_UnmountAndEjectVolume(BMessage* message)
691 {
692 	int32 id;
693 	if (message->FindInt32("id", &id) == B_OK) {
694 		BDiskDeviceRoster roster;
695 		BPartition *partition;
696 		BDiskDevice device;
697 		if (roster.GetPartitionWithID(id, &device, &partition) != B_OK)
698 			return;
699 
700 		BPath path;
701 		if (partition->GetMountPoint(&path) == B_OK)
702 			_UnmountAndEjectVolume(partition, path, partition->ContentName());
703 	} else {
704 		// see if we got a dev_t
705 
706 		dev_t device;
707 		if (message->FindInt32("device_id", &device) != B_OK)
708 			return;
709 
710 		BVolume volume(device);
711 		status_t status = volume.InitCheck();
712 
713 		char name[B_FILE_NAME_LENGTH];
714 		if (status == B_OK)
715 			status = volume.GetName(name);
716 		if (status < B_OK)
717 			snprintf(name, sizeof(name), "device:%ld", device);
718 
719 		BPath path;
720 		if (status == B_OK) {
721 			BDirectory mountPoint;
722 			status = volume.GetRootDirectory(&mountPoint);
723 			if (status == B_OK)
724 				status = path.SetTo(&mountPoint, ".");
725 		}
726 
727 		if (status == B_OK)
728 			_UnmountAndEjectVolume(NULL, path, name);
729 	}
730 }
731 
732 
733 void
734 AutoMounter::_FromMode(mount_mode mode, bool& all, bool& bfs, bool& restore)
735 {
736 	all = bfs = restore = false;
737 
738 	switch (mode) {
739 		case kAllVolumes:
740 			all = true;
741 			break;
742 		case kOnlyBFSVolumes:
743 			bfs = true;
744 			break;
745 		case kRestorePreviousVolumes:
746 			restore = true;
747 			break;
748 
749 		default:
750 			break;
751 	}
752 }
753 
754 
755 AutoMounter::mount_mode
756 AutoMounter::_ToMode(bool all, bool bfs, bool restore)
757 {
758 	if (all)
759 		return kAllVolumes;
760 	if (bfs)
761 		return kOnlyBFSVolumes;
762 	if (restore)
763 		return kRestorePreviousVolumes;
764 
765 	return kNoVolumes;
766 }
767 
768 
769 void
770 AutoMounter::_ReadSettings()
771 {
772 	BPath directoryPath;
773 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &directoryPath, true)
774 		!= B_OK) {
775 		return;
776 	}
777 
778 	BPath path(directoryPath);
779 	path.Append(kMountServerSettings);
780 	fPrefsFile.SetTo(path.Path(), O_RDWR);
781 
782 	if (fPrefsFile.InitCheck() != B_OK) {
783 		// no prefs file yet, create a new one
784 
785 		BDirectory dir(directoryPath.Path());
786 		dir.CreateFile(kMountServerSettings, &fPrefsFile);
787 		return;
788 	}
789 
790 	ssize_t settingsSize = (ssize_t)fPrefsFile.Seek(0, SEEK_END);
791 	if (settingsSize == 0)
792 		return;
793 
794 	ASSERT(settingsSize != 0);
795 	char *buffer = new(std::nothrow) char[settingsSize];
796 	if (buffer == NULL) {
797 		PRINT(("error writing automounter settings, out of memory\n"));
798 		return;
799 	}
800 
801 	fPrefsFile.Seek(0, 0);
802 	if (fPrefsFile.Read(buffer, (size_t)settingsSize) != settingsSize) {
803 		PRINT(("error reading automounter settings\n"));
804 		delete [] buffer;
805 		return;
806 	}
807 
808 	BMessage message('stng');
809 	status_t result = message.Unflatten(buffer);
810 	if (result != B_OK) {
811 		PRINT(("error %s unflattening automounter settings, size %" B_PRIdSSIZE "\n",
812 			strerror(result), settingsSize));
813 		delete [] buffer;
814 		return;
815 	}
816 
817 	delete [] buffer;
818 
819 	// update flags and modes from the message
820 	_UpdateSettingsFromMessage(&message);
821 	// copy the previously mounted partitions
822 	fSettings = message;
823 }
824 
825 
826 void
827 AutoMounter::_WriteSettings()
828 {
829 	if (fPrefsFile.InitCheck() != B_OK)
830 		return;
831 
832 	BMessage message('stng');
833 	_GetSettings(&message);
834 
835 	ssize_t settingsSize = message.FlattenedSize();
836 
837 	char *buffer = new(std::nothrow) char[settingsSize];
838 	if (buffer == NULL) {
839 		PRINT(("error writing automounter settings, out of memory\n"));
840 		return;
841 	}
842 
843 	status_t result = message.Flatten(buffer, settingsSize);
844 
845 	fPrefsFile.Seek(0, SEEK_SET);
846 	result = fPrefsFile.Write(buffer, (size_t)settingsSize);
847 	if (result != settingsSize)
848 		PRINT(("error writing automounter settings, %s\n", strerror(result)));
849 
850 	delete [] buffer;
851 }
852 
853 
854 void
855 AutoMounter::_UpdateSettingsFromMessage(BMessage* message)
856 {
857 	// auto mounter settings
858 
859 	bool all, bfs, restore;
860 	if (message->FindBool("autoMountAll", &all) != B_OK)
861 		all = true;
862 	if (message->FindBool("autoMountAllBFS", &bfs) != B_OK)
863 		bfs = false;
864 
865 	fRemovableMode = _ToMode(all, bfs, false);
866 
867 	// initial mount settings
868 
869 	if (message->FindBool("initialMountAll", &all) != B_OK)
870 		all = false;
871 	if (message->FindBool("initialMountAllBFS", &bfs) != B_OK)
872 		bfs = false;
873 	if (message->FindBool("initialMountRestore", &restore) != B_OK)
874 		restore = true;
875 
876 	fNormalMode = _ToMode(all, bfs, restore);
877 
878 	// eject settings
879 	bool eject;
880 	if (message->FindBool("ejectWhenUnmounting", &eject) == B_OK)
881 		fEjectWhenUnmounting = eject;
882 }
883 
884 
885 void
886 AutoMounter::_GetSettings(BMessage *message)
887 {
888 	message->MakeEmpty();
889 
890 	bool all, bfs, restore;
891 
892 	_FromMode(fNormalMode, all, bfs, restore);
893 	message->AddBool("initialMountAll", all);
894 	message->AddBool("initialMountAllBFS", bfs);
895 	message->AddBool("initialMountRestore", restore);
896 
897 	_FromMode(fRemovableMode, all, bfs, restore);
898 	message->AddBool("autoMountAll", all);
899 	message->AddBool("autoMountAllBFS", bfs);
900 
901 	message->AddBool("ejectWhenUnmounting", fEjectWhenUnmounting);
902 
903 	// Save mounted volumes so we can optionally mount them on next
904 	// startup
905 	BVolumeRoster volumeRoster;
906 	BVolume volume;
907 	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
908         fs_info info;
909         if (fs_stat_dev(volume.Device(), &info) == 0
910 			&& (info.flags & (B_FS_IS_REMOVABLE | B_FS_IS_PERSISTENT)) != 0) {
911 			message->AddString(info.device_name, info.volume_name);
912 
913 			BString mountFlagsKey(info.device_name);
914 			mountFlagsKey << kMountFlagsKeyExtension;
915 			uint32 mountFlags = 0;
916 			if (volume.IsReadOnly())
917 				mountFlags |= B_MOUNT_READ_ONLY;
918 			message->AddInt32(mountFlagsKey.String(), mountFlags);
919 		}
920 	}
921 }
922 
923 
924 // #pragma mark -
925 
926 
927 int
928 main(int argc, char* argv[])
929 {
930 	AutoMounter app;
931 
932 	app.Run();
933 	return 0;
934 }
935 
936 
937