xref: /haiku/src/system/kernel/fs/vfs_boot.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 /*
2  * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include <OS.h>
11 #include <fs_info.h>
12 
13 #include <disk_device_manager/KDiskDevice.h>
14 #include <disk_device_manager/KDiskDeviceManager.h>
15 #include <disk_device_manager/KPartitionVisitor.h>
16 #include <DiskDeviceTypes.h>
17 
18 #include <vfs.h>
19 #include <file_cache.h>
20 #include <KPath.h>
21 #include <syscalls.h>
22 #include <boot/kernel_args.h>
23 #include <util/Stack.h>
24 
25 #include <stdio.h>
26 
27 
28 //#define TRACE_VFS
29 #ifdef TRACE_VFS
30 #	define PRINT(x) dprintf x
31 #	define FUNCTION(x) dprintf x
32 #else
33 #	define PRINT(x) ;
34 #	define FUNCTION(x) ;
35 #endif
36 
37 
38 typedef Stack<KPartition *> PartitionStack;
39 
40 static struct {
41 	const char *path;
42 	const char *target;
43 } sPredefinedLinks[] = {
44 	{"/system", "/boot/beos/system"},
45 	{"/bin", "/boot/beos/bin"},
46 	{"/etc", "/boot/beos/etc"},
47 	{"/var", "/boot/var"},
48 	{"/tmp", "/boot/var/tmp"},
49 	{NULL}
50 };
51 
52 // This can be used by other code to see if there is a boot file system already
53 dev_t gBootDevice = -1;
54 
55 
56 /**	No image was chosen - prefer disks with names like "Haiku", or "System"
57  */
58 
59 static int
60 compare_image_boot(const void *_a, const void *_b)
61 {
62 	KPartition *a = *(KPartition **)_a;
63 	KPartition *b = *(KPartition **)_b;
64 
65 	if (a->ContentName() != NULL) {
66 		if (b->ContentName() == NULL)
67 			return 1;
68 	} else if (b->ContentName() != NULL) {
69 		return -1;
70 	} else
71 		return 0;
72 
73 	int compare = strcmp(a->ContentName(), b->ContentName());
74 	if (!compare)
75 		return 0;
76 
77 	if (!strcasecmp(a->ContentName(), "Haiku"))
78 		return 1;
79 	if (!strcasecmp(b->ContentName(), "Haiku"))
80 		return -1;
81 	if (!strncmp(a->ContentName(), "System", 6))
82 		return 1;
83 	if (!strncmp(b->ContentName(), "System", 6))
84 		return -1;
85 
86 	return compare;
87 }
88 
89 
90 /**	The system was booted from CD - prefer CDs over other entries. If there
91  *	is no CD, fall back to the standard mechanism (as implemented by
92  *	compare_image_boot().
93  */
94 
95 static int
96 compare_cd_boot(const void *_a, const void *_b)
97 {
98 	KPartition *a = *(KPartition **)_a;
99 	KPartition *b = *(KPartition **)_b;
100 
101 	bool aIsCD = a->Type() != NULL && !strcmp(a->Type(), kPartitionTypeDataSession);
102 	bool bIsCD = b->Type() != NULL && !strcmp(b->Type(), kPartitionTypeDataSession);
103 
104 	int compare = (int)aIsCD - (int)bIsCD;
105 	if (compare != 0)
106 		return compare;
107 
108 	return compare_image_boot(_a, _b);
109 }
110 
111 
112 static status_t
113 get_boot_partitions(kernel_args *args, PartitionStack &partitions)
114 {
115 	// make the boot partition (and probably others) available
116 	KDiskDeviceManager::CreateDefault();
117 	KDiskDeviceManager *manager = KDiskDeviceManager::Default();
118 
119 	status_t status = manager->InitialDeviceScan();
120 	if (status == B_OK) {
121 		// ToDo: do this for real... (no hacks allowed :))
122 		for (;;) {
123 			snooze(500000);
124 			if (manager->CountJobs() == 0)
125 				break;
126 		}
127 	} else {
128 		dprintf("KDiskDeviceManager::InitialDeviceScan() failed: %s\n", strerror(status));
129 		return status;
130 	}
131 
132 	// ToDo: do this for real! It will currently only use the partition offset;
133 	//	it does not yet use the disk_identifier information.
134 
135 	struct BootPartitionVisitor : KPartitionVisitor {
136 		BootPartitionVisitor(kernel_args &args, PartitionStack &stack)
137 			: fArgs(args), fPartitions(stack) {}
138 
139 		virtual bool VisitPre(KPartition *partition)
140 		{
141 			if (!partition->ContainsFileSystem())
142 				return false;
143 
144 			if (!fArgs.boot_disk.booted_from_image) {
145 				// the simple case: we can just boot from the selected boot device
146 				if (partition->Offset() == fArgs.boot_disk.partition_offset) {
147 					fPartitions.Push(partition);
148 					return true;
149 				}
150 			} else {
151 				// for now, we will just collect all BFS volumes
152 				if (fArgs.boot_disk.cd && fArgs.boot_disk.user_selected
153 					&& partition->Type() != NULL
154 					&& strcmp(partition->Type(), kPartitionTypeDataSession))
155 					return false;
156 
157 				if (partition->ContentType() != NULL
158 					&& !strcmp(partition->ContentType(), "Be File System"))
159 					fPartitions.Push(partition);
160 			}
161 			return false;
162 		}
163 		private:
164 			kernel_args		&fArgs;
165 			PartitionStack	&fPartitions;
166 	} visitor(*args, partitions);
167 
168 	KDiskDevice *device;
169 	int32 cookie = 0;
170 	while ((device = manager->NextDevice(&cookie)) != NULL) {
171 		if (device->VisitEachDescendant(&visitor) != NULL)
172 			break;
173 	}
174 
175 	if (!args->boot_disk.user_selected) {
176 		// sort partition list (ie. when booting from CD, CDs should come first in the list)
177 		qsort(partitions.Array(), partitions.CountItems(), sizeof(KPartition *),
178 			args->boot_disk.cd ? compare_cd_boot : compare_image_boot);
179 	}
180 
181 	return B_OK;
182 }
183 
184 
185 //	#pragma mark -
186 
187 
188 status_t
189 vfs_bootstrap_file_systems(void)
190 {
191 	status_t status;
192 
193 	// bootstrap the root filesystem
194 	status = _kern_mount("/", NULL, "rootfs", 0, NULL);
195 	if (status < B_OK)
196 		panic("error mounting rootfs!\n");
197 
198 	_kern_setcwd(-1, "/");
199 
200 	// bootstrap the devfs
201 	_kern_create_dir(-1, "/dev", 0755);
202 	status = _kern_mount("/dev", NULL, "devfs", 0, NULL);
203 	if (status < B_OK)
204 		panic("error mounting devfs\n");
205 
206 	// bootstrap the pipefs
207 	_kern_create_dir(-1, "/pipe", 0755);
208 	status = _kern_mount("/pipe", NULL, "pipefs", 0, NULL);
209 	if (status < B_OK)
210 		panic("error mounting pipefs\n");
211 
212 	// create directory for the boot volume
213 	_kern_create_dir(-1, "/boot", 0755);
214 
215 	// create some standard links on the rootfs
216 
217 	for (int32 i = 0; sPredefinedLinks[i].path != NULL; i++) {
218 		_kern_create_symlink(-1, sPredefinedLinks[i].path,
219 			sPredefinedLinks[i].target, 0);
220 			// we don't care if it will succeed or not
221 	}
222 
223 	return B_OK;
224 }
225 
226 
227 status_t
228 vfs_mount_boot_file_system(kernel_args *args)
229 {
230 	PartitionStack partitions;
231 	status_t status = get_boot_partitions(args, partitions);
232 	if (status < B_OK) {
233 		panic("Did not get any boot partitions!");
234 		return status;
235 	}
236 
237 	KPartition *bootPartition;
238 	while (partitions.Pop(&bootPartition)) {
239 		KPath path;
240 		if (bootPartition->GetPath(&path) != B_OK)
241 			panic("could not get boot device!\n");
242 
243 		gBootDevice = _kern_mount("/boot", path.Path(), NULL, 0, NULL);
244 		if (gBootDevice >= B_OK)
245 			break;
246 	}
247 
248 	if (gBootDevice < B_OK)
249 		panic("could not mount boot device!\n");
250 
251 	// create link for the name of the boot device
252 
253 	fs_info info;
254 	if (_kern_read_fs_info(gBootDevice, &info) == B_OK) {
255 		char path[B_FILE_NAME_LENGTH + 1];
256 		snprintf(path, sizeof(path), "/%s", info.volume_name);
257 
258 		_kern_create_symlink(-1, path, "/boot", 0);
259 	}
260 
261 	file_cache_init_post_boot_device();
262 	return B_OK;
263 }
264 
265