vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / fs / vfs_boot.cpp
blob3e5a9d94e43e6ea83a1a833bd9b9eed5691765d0
1 /*
2 * Copyright 2007-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
11 #include "vfs_boot.h"
13 #include <stdio.h>
14 #include <strings.h>
16 #include <fs_info.h>
17 #include <OS.h>
19 #include <boot/kernel_args.h>
20 #include <directories.h>
21 #include <disk_device_manager/KDiskDevice.h>
22 #include <disk_device_manager/KDiskDeviceManager.h>
23 #include <disk_device_manager/KPartitionVisitor.h>
24 #include <DiskDeviceTypes.h>
25 #include <file_cache.h>
26 #include <fs/KPath.h>
27 #include <kmodule.h>
28 #include <syscalls.h>
29 #include <util/KMessage.h>
30 #include <util/Stack.h>
31 #include <vfs.h>
33 #include "vfs_net_boot.h"
36 //#define TRACE_VFS
37 #ifdef TRACE_VFS
38 # define TRACE(x) dprintf x
39 #else
40 # define TRACE(x) ;
41 #endif
44 typedef Stack<KPartition *> PartitionStack;
46 static struct {
47 const char *path;
48 const char *target;
49 } sPredefinedLinks[] = {
50 { kGlobalSystemDirectory, kSystemDirectory },
51 { kGlobalBinDirectory, kSystemBinDirectory },
52 { kGlobalEtcDirectory, kSystemEtcDirectory },
53 { kGlobalTempDirectory, kSystemTempDirectory },
54 { kGlobalVarDirectory, kSystemVarDirectory },
55 { kGlobalPackageLinksDirectory, kSystemPackageLinksDirectory },
56 {NULL}
59 // This can be used by other code to see if there is a boot file system already
60 dev_t gBootDevice = -1;
61 bool gReadOnlyBootDevice = false;
64 /*! No image was chosen - prefer disks with names like "Haiku", or "System"
66 int
67 compare_image_boot(const void* _a, const void* _b)
69 KPartition* a = *(KPartition**)_a;
70 KPartition* b = *(KPartition**)_b;
72 if (a->ContentName() != NULL) {
73 if (b->ContentName() == NULL)
74 return 1;
75 } else if (b->ContentName() != NULL) {
76 return -1;
77 } else
78 return 0;
80 int compare = strcasecmp(a->ContentName(), b->ContentName());
81 if (!compare)
82 return 0;
84 if (!strcasecmp(a->ContentName(), "Haiku"))
85 return 1;
86 if (!strcasecmp(b->ContentName(), "Haiku"))
87 return -1;
88 if (!strncmp(a->ContentName(), "System", 6))
89 return 1;
90 if (!strncmp(b->ContentName(), "System", 6))
91 return -1;
93 return compare;
97 /*! The system was booted from CD - prefer CDs over other entries. If there
98 is no CD, fall back to the standard mechanism (as implemented by
99 compare_image_boot().
101 static int
102 compare_cd_boot(const void* _a, const void* _b)
104 KPartition* a = *(KPartition**)_a;
105 KPartition* b = *(KPartition**)_b;
107 bool aIsCD = a->Type() != NULL
108 && !strcmp(a->Type(), kPartitionTypeDataSession);
109 bool bIsCD = b->Type() != NULL
110 && !strcmp(b->Type(), kPartitionTypeDataSession);
112 int compare = (int)aIsCD - (int)bIsCD;
113 if (compare != 0)
114 return compare;
116 return compare_image_boot(_a, _b);
120 /*! Computes a check sum for the specified block.
121 The check sum is the sum of all data in that block interpreted as an
122 array of uint32 values.
123 Note, this must use the same method as the one used in
124 boot/platform/bios_ia32/devices.cpp (or similar solutions).
126 static uint32
127 compute_check_sum(KDiskDevice* device, off_t offset)
129 char buffer[512];
130 ssize_t bytesRead = read_pos(device->FD(), offset, buffer, sizeof(buffer));
131 if (bytesRead < B_OK)
132 return 0;
134 if (bytesRead < (ssize_t)sizeof(buffer))
135 memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
137 uint32* array = (uint32*)buffer;
138 uint32 sum = 0;
140 for (uint32 i = 0;
141 i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) {
142 sum += array[i];
145 return sum;
149 // #pragma mark - BootMethod
152 BootMethod::BootMethod(const KMessage& bootVolume, int32 method)
154 fBootVolume(bootVolume),
155 fMethod(method)
160 BootMethod::~BootMethod()
165 status_t
166 BootMethod::Init()
168 return B_OK;
172 // #pragma mark - DiskBootMethod
175 class DiskBootMethod : public BootMethod {
176 public:
177 DiskBootMethod(const KMessage& bootVolume, int32 method)
178 : BootMethod(bootVolume, method)
182 virtual bool IsBootDevice(KDiskDevice* device, bool strict);
183 virtual bool IsBootPartition(KPartition* partition, bool& foundForSure);
184 virtual void SortPartitions(KPartition** partitions, int32 count);
188 bool
189 DiskBootMethod::IsBootDevice(KDiskDevice* device, bool strict)
191 disk_identifier* disk;
192 int32 diskIdentifierSize;
193 if (fBootVolume.FindData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
194 (const void**)&disk, &diskIdentifierSize) != B_OK) {
195 dprintf("DiskBootMethod::IsBootDevice(): no disk identifier!\n");
196 return false;
199 TRACE(("boot device: bus %" B_PRId32 ", device %" B_PRId32 "\n",
200 disk->bus_type, disk->device_type));
202 // Assume that CD boots only happen off removable media.
203 if (fMethod == BOOT_METHOD_CD && !device->IsRemovable())
204 return false;
206 switch (disk->bus_type) {
207 case PCI_BUS:
208 case LEGACY_BUS:
209 // TODO: implement this! (and then enable this feature in the boot
210 // loader)
211 // (we need a way to get the device_node of a device, then)
212 break;
214 case UNKNOWN_BUS:
215 // nothing to do here
216 break;
219 switch (disk->device_type) {
220 case UNKNOWN_DEVICE:
221 // test if the size of the device matches
222 // (the BIOS might have given us the wrong value here, though)
223 if (strict && device->Size() != disk->device.unknown.size)
224 return false;
226 // Skip the check sum test for CDs, since we didn't read anything
227 // useful from the disk in the boot loader.
228 if (fMethod == BOOT_METHOD_CD)
229 break;
231 // check if the check sums match, too
232 for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
233 if (disk->device.unknown.check_sums[i].offset == -1)
234 continue;
236 if (compute_check_sum(device,
237 disk->device.unknown.check_sums[i].offset)
238 != disk->device.unknown.check_sums[i].sum) {
239 return false;
242 break;
244 case ATA_DEVICE:
245 case ATAPI_DEVICE:
246 case SCSI_DEVICE:
247 case USB_DEVICE:
248 case FIREWIRE_DEVICE:
249 case FIBRE_DEVICE:
250 // TODO: implement me!
251 break;
254 return true;
258 bool
259 DiskBootMethod::IsBootPartition(KPartition* partition, bool& foundForSure)
261 off_t bootPartitionOffset = fBootVolume.GetInt64(
262 BOOT_VOLUME_PARTITION_OFFSET, 0);
264 if (!fBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) {
265 // the simple case: we can just boot from the selected boot
266 // device
267 if (partition->Offset() == bootPartitionOffset) {
268 dprintf("Identified boot partition by partition offset.\n");
269 foundForSure = true;
270 return true;
272 } else {
273 // For now, unless we can positively identify an anyboot CD, we will
274 // just collect all BFS/ISO9660 volumes.
276 if (fMethod == BOOT_METHOD_CD) {
277 // Check for the boot partition of an anyboot CD. We identify it as
278 // such, if it is the only primary partition on the CD, has type
279 // BFS, and the boot partition offset is 0.
280 KDiskDevice* device = partition->Device();
281 if (IsBootDevice(device, false) && bootPartitionOffset == 0
282 && partition->Parent() == device && device->CountChildren() == 1
283 && device->ContentType() != NULL
284 && strcmp(device->ContentType(), kPartitionTypeIntel) == 0
285 && partition->ContentType() != NULL
286 && strcmp(partition->ContentType(), kPartitionTypeBFS) == 0) {
287 dprintf("Identified anyboot CD.\n");
288 foundForSure = true;
289 return true;
292 // Ignore non-session partitions, if a boot partition was selected
293 // by the user.
294 if (fBootVolume.GetBool(BOOT_VOLUME_USER_SELECTED, false)
295 && partition->Type() != NULL
296 && strcmp(partition->Type(), kPartitionTypeDataSession) != 0) {
297 return false;
301 if (partition->ContentType() != NULL
302 && (strcmp(partition->ContentType(), kPartitionTypeBFS) == 0
303 || strcmp(partition->ContentType(), kPartitionTypeISO9660) == 0)) {
304 return true;
308 return false;
312 void
313 DiskBootMethod::SortPartitions(KPartition** partitions, int32 count)
315 qsort(partitions, count, sizeof(KPartition*),
316 fMethod == BOOT_METHOD_CD ? compare_cd_boot : compare_image_boot);
320 // #pragma mark -
323 /*! Make the boot partition (and probably others) available.
324 The partitions that are a boot candidate a put into the /a partitions
325 stack. If the user selected a boot device, there is will only be one
326 entry in this stack; if not, the most likely is put up first.
327 The boot code should then just try them one by one.
329 static status_t
330 get_boot_partitions(KMessage& bootVolume, PartitionStack& partitions)
332 dprintf("get_boot_partitions(): boot volume message:\n");
333 bootVolume.Dump(&dprintf);
335 // create boot method
336 int32 bootMethodType = bootVolume.GetInt32(BOOT_METHOD, BOOT_METHOD_DEFAULT);
337 dprintf("get_boot_partitions(): boot method type: %" B_PRId32 "\n",
338 bootMethodType);
340 BootMethod* bootMethod = NULL;
341 switch (bootMethodType) {
342 case BOOT_METHOD_NET:
343 bootMethod = new(nothrow) NetBootMethod(bootVolume, bootMethodType);
344 break;
346 case BOOT_METHOD_HARD_DISK:
347 case BOOT_METHOD_CD:
348 default:
349 bootMethod = new(nothrow) DiskBootMethod(bootVolume,
350 bootMethodType);
351 break;
354 status_t status = bootMethod != NULL ? bootMethod->Init() : B_NO_MEMORY;
355 if (status != B_OK)
356 return status;
358 KDiskDeviceManager::CreateDefault();
359 KDiskDeviceManager *manager = KDiskDeviceManager::Default();
361 status = manager->InitialDeviceScan();
362 if (status != B_OK) {
363 dprintf("KDiskDeviceManager::InitialDeviceScan() failed: %s\n",
364 strerror(status));
365 return status;
368 if (1 /* dump devices and partitions */) {
369 KDiskDevice *device;
370 int32 cookie = 0;
371 while ((device = manager->NextDevice(&cookie)) != NULL) {
372 device->Dump(true, 0);
376 struct BootPartitionVisitor : KPartitionVisitor {
377 BootPartitionVisitor(BootMethod* bootMethod, PartitionStack &stack)
378 : fPartitions(stack),
379 fBootMethod(bootMethod)
383 virtual bool VisitPre(KPartition *partition)
385 if (!partition->ContainsFileSystem())
386 return false;
388 bool foundForSure = false;
389 if (fBootMethod->IsBootPartition(partition, foundForSure))
390 fPartitions.Push(partition);
392 // if found for sure, we can terminate the search
393 return foundForSure;
396 private:
397 PartitionStack &fPartitions;
398 BootMethod* fBootMethod;
399 } visitor(bootMethod, partitions);
401 bool strict = true;
403 while (true) {
404 KDiskDevice *device;
405 int32 cookie = 0;
406 while ((device = manager->NextDevice(&cookie)) != NULL) {
407 if (!bootMethod->IsBootDevice(device, strict))
408 continue;
410 if (device->VisitEachDescendant(&visitor) != NULL)
411 break;
414 if (!partitions.IsEmpty() || !strict)
415 break;
417 // we couldn't find any potential boot devices, try again less strict
418 strict = false;
421 // sort partition list (e.g.. when booting from CD, CDs should come first in
422 // the list)
423 if (!bootVolume.GetBool(BOOT_VOLUME_USER_SELECTED, false))
424 bootMethod->SortPartitions(partitions.Array(), partitions.CountItems());
426 return B_OK;
430 // #pragma mark -
433 status_t
434 vfs_bootstrap_file_systems(void)
436 status_t status;
438 // bootstrap the root filesystem
439 status = _kern_mount("/", NULL, "rootfs", 0, NULL, 0);
440 if (status < B_OK)
441 panic("error mounting rootfs!\n");
443 _kern_setcwd(-1, "/");
445 // bootstrap the devfs
446 _kern_create_dir(-1, "/dev", 0755);
447 status = _kern_mount("/dev", NULL, "devfs", 0, NULL, 0);
448 if (status < B_OK)
449 panic("error mounting devfs\n");
451 // create directory for the boot volume
452 _kern_create_dir(-1, "/boot", 0755);
454 // create some standard links on the rootfs
456 for (int32 i = 0; sPredefinedLinks[i].path != NULL; i++) {
457 _kern_create_symlink(-1, sPredefinedLinks[i].path,
458 sPredefinedLinks[i].target, 0777);
459 // we don't care if it will succeed or not
462 return B_OK;
466 void
467 vfs_mount_boot_file_system(kernel_args* args)
469 KMessage bootVolume;
470 bootVolume.SetTo(args->boot_volume, args->boot_volume_size);
472 PartitionStack partitions;
473 status_t status = get_boot_partitions(bootVolume, partitions);
474 if (status < B_OK) {
475 panic("get_boot_partitions failed!");
477 if (partitions.IsEmpty()) {
478 panic("did not find any boot partitions!");
481 dev_t bootDevice = -1;
483 KPartition* bootPartition;
484 while (partitions.Pop(&bootPartition)) {
485 KPath path;
486 if (bootPartition->GetPath(&path) != B_OK)
487 panic("could not get boot device!\n");
489 const char* fsName = NULL;
490 bool readOnly = false;
491 if (strcmp(bootPartition->ContentType(), kPartitionTypeISO9660) == 0) {
492 fsName = "iso9660:write_overlay:attribute_overlay";
493 readOnly = true;
494 } else if (bootPartition->IsReadOnly()
495 && strcmp(bootPartition->ContentType(), kPartitionTypeBFS) == 0) {
496 fsName = "bfs:write_overlay";
497 readOnly = true;
500 TRACE(("trying to mount boot partition: %s\n", path.Path()));
502 bootDevice = _kern_mount("/boot", path.Path(), fsName, 0, NULL, 0);
503 if (bootDevice >= 0) {
504 dprintf("Mounted boot partition: %s\n", path.Path());
505 gReadOnlyBootDevice = readOnly;
506 break;
510 if (bootDevice < B_OK)
511 panic("could not mount boot device!\n");
513 // create link for the name of the boot device
515 fs_info info;
516 if (_kern_read_fs_info(bootDevice, &info) == B_OK) {
517 char path[B_FILE_NAME_LENGTH + 1];
518 snprintf(path, sizeof(path), "/%s", info.volume_name);
520 _kern_create_symlink(-1, path, "/boot", 0777);
523 // If we're booting off a packaged system, mount packagefs.
524 struct stat st;
525 if (bootVolume.GetBool(BOOT_VOLUME_PACKAGED, false)
526 || (bootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)
527 && lstat(kSystemPackagesDirectory, &st) == 0)) {
528 static const char* const kPackageFSName = "packagefs";
530 char arguments[256];
531 strlcpy(arguments, "packages /boot/system/packages; type system",
532 sizeof(arguments));
533 if (const char* stateName
534 = bootVolume.GetString(BOOT_VOLUME_PACKAGES_STATE, NULL)) {
535 strlcat(arguments, "; state ", sizeof(arguments));
536 strlcat(arguments, stateName, sizeof(arguments));
539 dev_t packageMount = _kern_mount("/boot/system", NULL, kPackageFSName,
540 0, arguments, 0 /* unused argument length */);
541 if (packageMount < 0) {
542 panic("Failed to mount system packagefs: %s",
543 strerror(packageMount));
546 packageMount = _kern_mount("/boot/home/config", NULL, kPackageFSName, 0,
547 "packages /boot/home/config/packages; type home",
548 0 /* unused argument length */);
549 if (packageMount < 0) {
550 dprintf("Failed to mount home packagefs: %s\n",
551 strerror(packageMount));
555 // Now that packagefs is mounted, the boot volume is really ready.
556 gBootDevice = bootDevice;
558 // Do post-boot-volume module initialization. The module code wants to know
559 // whether the module images the boot loader has pre-loaded are the same as
560 // on the boot volume. That is the case when booting from hard disk or CD,
561 // but not via network.
562 int32 bootMethodType = bootVolume.GetInt32(BOOT_METHOD, BOOT_METHOD_DEFAULT);
563 bool bootingFromBootLoaderVolume = bootMethodType == BOOT_METHOD_HARD_DISK
564 || bootMethodType == BOOT_METHOD_CD;
565 module_init_post_boot_device(bootingFromBootLoaderVolume);
567 file_cache_init_post_boot_device();
569 // search for other disk systems
570 KDiskDeviceManager *manager = KDiskDeviceManager::Default();
571 manager->RescanDiskSystems();
572 manager->StartMonitoring();