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