From 80b2da5e53ba827b90cf70374c0aaf73cd95abf3 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 5 Feb 2008 20:54:42 +0000 Subject: [PATCH] Patch by Samuel Rodriguez Perez: Added support for writing Haiku directly onto a device under FreeBSD. I messed around with the code a little (style-fixes, some refactoring) without being able to compile or test it, so be careful... git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23880 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../platform/bios_ia32/makebootable.cpp | 99 ++++++- src/build/libroot/Jamfile | 7 + src/build/libroot/fs.cpp | 38 ++- src/build/libroot/fs_freebsd.cpp | 254 ++++++++++++++++++ src/build/libroot/fs_freebsd.h | 17 ++ src/tools/fs_shell/uio.cpp | 12 +- src/tools/fs_shell/unistd.cpp | 64 ++++- 7 files changed, 476 insertions(+), 15 deletions(-) create mode 100644 src/build/libroot/fs_freebsd.cpp create mode 100644 src/build/libroot/fs_freebsd.h diff --git a/src/bin/makebootable/platform/bios_ia32/makebootable.cpp b/src/bin/makebootable/platform/bios_ia32/makebootable.cpp index c1aef87524..c206d5e6a0 100644 --- a/src/bin/makebootable/platform/bios_ia32/makebootable.cpp +++ b/src/bin/makebootable/platform/bios_ia32/makebootable.cpp @@ -19,7 +19,7 @@ #include #include -// Linux support +// Linux and FreeBSD support #ifdef HAIKU_HOST_PLATFORM_LINUX # include # include @@ -27,7 +27,15 @@ # include "PartitionMap.h" # include "PartitionMapParser.h" -#endif // HAIKU_HOST_PLATFORM_LINUX +#elif HAIKU_HOST_PLATFORM_FREEBSD +# include +# include +# include +# include + +# include "PartitionMap.h" +# include "PartitionMapParser.h" +#endif static const char *kCommandName = "makebootable"; @@ -272,13 +280,98 @@ main(int argc, const char *const *argv) noPartition = true; } else if (S_ISCHR(st.st_mode)) { // character special: a device or partition under BeOS - #ifndef __BEOS__ + // or under FreeBSD + #if !defined(__BEOS__) && !defined(HAIKU_HOST_PLATFORM_FREEBSD) fprintf(stderr, "Error: Character special devices not " "supported on this platform.\n"); exit(1); #endif + + #ifdef HAIKU_HOST_PLATFORM_FREEBSD + + // chop off the trailing number + int fileNameLen = strlen(fileName); + int baseNameLen = -1; + for (int k = fileNameLen - 1; k >= 0; k--) { + if (!isdigit(fileName[k])) { + baseNameLen = k + 1; + break; + } + } + + // Remove de 's' from 'ad2s2' slice device (partition for DOS + // users) to get 'ad2' base device + baseNameLen--; + + if (baseNameLen < 0) { + // only digits? + fprintf(stderr, "Error: Failed to get base device name.\n"); + exit(1); + } + + if (baseNameLen < fileNameLen) { + // get base device name and partition index + char baseDeviceName[B_PATH_NAME_LENGTH]; + int partitionIndex = atoi(fileName + baseNameLen + 1); + // Don't forget the 's' of slice :) + memcpy(baseDeviceName, fileName, baseNameLen); + baseDeviceName[baseNameLen] = '\0'; + + // open base device + int baseFD = open(baseDeviceName, O_RDONLY); + if (baseFD < 0) { + fprintf(stderr, "Error: Failed to open \"%s\": %s\n", + baseDeviceName, strerror(errno)); + exit(1); + } + + // get device size + int64 deviceSize; + if (ioctl(baseFD, DIOCGMEDIASIZE, &deviceSize) == -1) { + fprintf(stderr, "Error: Failed to get device geometry " + "for \"%s\": %s\n", baseDeviceName, + strerror(errno)); + exit(1); + } + + // parse the partition map + PartitionMapParser parser(baseFD, 0, deviceSize); + PartitionMap map; + error = parser.Parse(NULL, &map); + if (error != B_OK) { + fprintf(stderr, "Error: Parsing partition table on " + "device \"%s\" failed: %s\n", baseDeviceName, + strerror(error)); + exit(1); + } + + close(baseFD); + + // check the partition we are supposed to write at + Partition *partition = map.PartitionAt(partitionIndex - 1); + if (!partition || partition->IsEmpty()) { + fprintf(stderr, "Error: Invalid partition index %d.\n", + partitionIndex); + exit(1); + } + + if (partition->IsExtended()) { + fprintf(stderr, "Error: Partition %d is an extended " + "partition.\n", partitionIndex); + exit(1); + } + + partitionOffset = partition->Offset(); + + } else { + // The given device is the base device. We'll write at + // offset 0. + } + + #endif // HAIKU_HOST_PLATFORM_FREEBSD + } else if (S_ISBLK(st.st_mode)) { // block device: a device or partition under Linux #ifdef HAIKU_HOST_PLATFORM_LINUX diff --git a/src/build/libroot/Jamfile b/src/build/libroot/Jamfile index 4faf9d6584..253562faba 100644 --- a/src/build/libroot/Jamfile +++ b/src/build/libroot/Jamfile @@ -27,6 +27,11 @@ if $(HOST_PLATFORM) != darwin { strlSources = strlcpy.c strlcat.c ; } +local hostPlatformSources ; +if $(HOST_PLATFORM) = freebsd { + hostPlatformSources = fs_freebsd.cpp ; +} + BuildPlatformSharedLibrary libroot_build.so : atomic.cpp byteorder.cpp @@ -38,6 +43,8 @@ BuildPlatformSharedLibrary libroot_build.so : sem.cpp thread.cpp + $(hostPlatformSources) + $(strlSources) strnlen.c diff --git a/src/build/libroot/fs.cpp b/src/build/libroot/fs.cpp index 713dae83f8..a542606606 100644 --- a/src/build/libroot/fs.cpp +++ b/src/build/libroot/fs.cpp @@ -1,3 +1,7 @@ +/* + * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ #include @@ -22,10 +26,28 @@ #include "fs_descriptors.h" #include "NodeRef.h" +#if defined(HAIKU_HOST_PLATFORM_FREEBSD) +# include "fs_freebsd.h" +#endif + + using namespace std; using namespace BPrivate; +#if defined(HAIKU_HOST_PLATFORM_FREEBSD) +# define haiku_host_platform_read haiku_freebsd_read +# define haiku_host_platform_write haiku_freebsd_write +# define haiku_host_platform_readv haiku_freebsd_readv +# define haiku_host_platform_writev haiku_freebsd_writev +#else +# define haiku_host_platform_read read +# define haiku_host_platform_write write +# define haiku_host_platform_readv readv +# define haiku_host_platform_writev writev +#endif + + static status_t get_path(dev_t device, ino_t node, const char *name, string &path); @@ -583,7 +605,8 @@ _kern_read(int fd, off_t pos, void *buffer, size_t bufferSize) } // read - ssize_t bytesRead = read(descriptor->fd, buffer, bufferSize); + ssize_t bytesRead = haiku_host_platform_read(descriptor->fd, buffer, + bufferSize); if (bytesRead < 0) return errno; @@ -608,7 +631,8 @@ _kern_write(int fd, off_t pos, const void *buffer, size_t bufferSize) } // read - ssize_t bytesWritten = write(descriptor->fd, buffer, bufferSize); + ssize_t bytesWritten = haiku_host_platform_write(descriptor->fd, buffer, + bufferSize); if (bytesWritten < 0) return errno; @@ -932,7 +956,7 @@ read_pos(int fd, off_t pos, void *buffer, size_t bufferSize) return errno; // read - ssize_t bytesRead = read(fd, buffer, bufferSize); + ssize_t bytesRead = haiku_host_platform_read(fd, buffer, bufferSize); if (bytesRead < 0) { errno = bytesRead; return -1; @@ -949,9 +973,9 @@ write_pos(int fd, off_t pos, const void *buffer, size_t bufferSize) off_t result = lseek(fd, pos, SEEK_SET); if (result < 0) return errno; - + // read - ssize_t bytesWritten = write(fd, buffer, bufferSize); + ssize_t bytesWritten = haiku_host_platform_write(fd, buffer, bufferSize); if (bytesWritten < 0) { errno = bytesWritten; return -1; @@ -970,7 +994,7 @@ readv_pos(int fd, off_t pos, const struct iovec *vec, size_t count) return errno; // read - ssize_t bytesRead = readv(fd, vec, count); + ssize_t bytesRead = haiku_host_platform_readv(fd, vec, count); if (bytesRead < 0) { errno = bytesRead; return -1; @@ -989,7 +1013,7 @@ writev_pos(int fd, off_t pos, const struct iovec *vec, size_t count) return errno; // read - ssize_t bytesWritten = writev(fd, vec, count); + ssize_t bytesWritten = haiku_host_platform_writev(fd, vec, count); if (bytesWritten < 0) { errno = bytesWritten; return -1; diff --git a/src/build/libroot/fs_freebsd.cpp b/src/build/libroot/fs_freebsd.cpp new file mode 100644 index 0000000000..e6fc60ff21 --- /dev/null +++ b/src/build/libroot/fs_freebsd.cpp @@ -0,0 +1,254 @@ +/* + * Copyright 2008, Samuel Rodriguez Perez, samuelgaliza@gmail.com. + * Distributed under the terms of the MIT License. + */ + +#include "fs_freebsd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// Read and write operations in FreeBSD only work on devices block by block. + +ssize_t +haiku_freebsd_read(int fd, void *buf, size_t nbytes) +{ + struct stat st; + if (fstat(fd, &st) != 0) + return -1; + + if (S_ISREG(st.st_mode)) + return read(fd, buf, nbytes); // Is a file! Good :) + + int sectorSize; + if (ioctl(fd, DIOCGSECTORSIZE, §orSize) == -1) + sectorSize = 512; // If fail, hardcode to 512 for now + + off_t cur = lseek(fd, 0, SEEK_CUR); + if (cur == -1) + perror("lseek 1"); + + off_t seekDiff = (sectorSize - (cur % sectorSize)) % sectorSize; + off_t nbytesDiff = (nbytes - seekDiff) % sectorSize; + + if (seekDiff == 0 && nbytesDiff == 0) { + // Not needed but this saves malloc and free operations + return read(fd, buf, nbytes); + + } else if (cur % sectorSize + nbytes <= sectorSize) { + // Read complete in only a block + char* tmpBlock = (char*)malloc(sectorSize); + + // Put at start of the block + off_t sdCur = lseek(fd, -(cur % sectorSize), SEEK_CUR); + if (sdCur == -1) + perror("lseek oneblock"); + if (read(fd, tmpBlock, sectorSize) == -1) + perror("read oneblock"); + memcpy((char*)buf, tmpBlock + cur % sectorSize, nbytes); + // repos at byte offset of latest wrote block + if (lseek(fd, -sectorSize + (cur % sectorSize) + nbytes, SEEK_CUR) + == -1) { + perror("lseek2 oneblock"); + } + + free(tmpBlock); + + return nbytes; + + } else { + // Needs to write more than a block + + char* tmpBlock = (char*)malloc(sectorSize); + + // First block if seek isn't + if (seekDiff > 0) { + // read entire block at 0 pos + if (lseek(fd, -(sectorSize - seekDiff), SEEK_CUR) == -1) + perror("lseek seekDiff"); + off_t sdCur = lseek(fd,0,SEEK_CUR); + if (sdCur == -1) + perror("lseek2 seekDiff"); + if (read(fd, tmpBlock, sectorSize) == -1 ) + perror("read seekDiff"); + // alter content + memcpy(buf, tmpBlock + (sectorSize - seekDiff), seekDiff); + + } + + // Blocks between + if ((nbytes - seekDiff) >= sectorSize) { + if (read(fd, ((char*)buf) + seekDiff, nbytes - seekDiff - nbytesDiff) == -1) + perror("read between"); + } + + // Last block if overflow + if (nbytesDiff > 0 ) { + + off_t sdCur = lseek(fd, 0, SEEK_CUR); + if (sdCur == -1) + perror("lseek last"); + if (read(fd, tmpBlock, sectorSize) == -1) + perror("read last"); + memcpy(((char*)buf) + nbytes - nbytesDiff, tmpBlock, nbytesDiff); + // repos at byte offset of latest wrote block + if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1) + perror("lseek2 last"); + } + + free(tmpBlock); + + return nbytes; + } + +} + + +ssize_t +haiku_freebsd_write(int fd, const void *buf, size_t nbytes) +{ + struct stat st; + if (fstat(fd, &st) != 0) + return -1; + + if (S_ISREG(st.st_mode)) + return write(fd, buf, nbytes); // Is a file! Good :) + + int sectorSize; + if (ioctl(fd, DIOCGSECTORSIZE, §orSize) == -1) + sectorSize = 512; // If fail, hardcode do 512 for now + + off_t cur = lseek(fd, 0, SEEK_CUR); + if (cur == -1) + perror("lseek 1"); + + off_t seekDiff = (sectorSize - (cur % sectorSize)) % sectorSize; + off_t nbytesDiff = (nbytes - seekDiff) % sectorSize; + + if (seekDiff == 0 && nbytesDiff == 0) { + // Not needed but this saves malloc and free operations + return write(fd, buf, nbytes); + + } else if (cur % sectorSize + nbytes <= sectorSize) { + // Write complete in only a block + char* tmpBlock = (char*)malloc(sectorSize); + + // Put at start of the block + off_t sdCur = lseek(fd, -(cur % sectorSize), SEEK_CUR); + if (sdCur == -1) + perror("lseek oneblock"); + if (pread(fd, tmpBlock, sectorSize, sdCur) == -1) + perror("pread oneblock"); + memcpy(tmpBlock + cur % sectorSize, (char*)buf, nbytes); + if (write(fd, tmpBlock, sectorSize) == -1) + perror("write oneblock"); + // repos at byte offset of latest written block + if (lseek(fd, -sectorSize + (cur % sectorSize) + nbytes, SEEK_CUR) == -1) + perror("lseek2 oneblock"); + + free(tmpBlock); + + return nbytes; + + } else { + // Needs to write more than a block + + char* tmpBlock = (char*)malloc(sectorSize); + + // First block if seek isn't + if (seekDiff > 0) { + // read entire block at 0 pos + if (lseek(fd, -(sectorSize - seekDiff), SEEK_CUR) == -1) + perror("lseek seekDiff"); + off_t sdCur = lseek(fd, 0, SEEK_CUR); + if (sdCur == -1) + perror("lseek2 seekDiff"); + if (pread(fd, tmpBlock, sectorSize, sdCur) == -1 ) + perror("pread seekDiff"); + // alter content + memcpy(tmpBlock + (sectorSize - seekDiff), buf, seekDiff); + if (write(fd, tmpBlock, sectorSize)==-1) + perror("write seekDiff"); + + } + + // Blocks between + if ((nbytes - seekDiff) >= sectorSize) { + if (write(fd, ((char*)buf) + seekDiff, nbytes - seekDiff - nbytesDiff) == -1) + perror("write between"); + } + + // Last block if overflow + if (nbytesDiff > 0) { + + off_t sdCur = lseek(fd, 0, SEEK_CUR); + if (sdCur == -1) + perror("lseek last"); + if (pread(fd, tmpBlock, sectorSize, sdCur) == -1) + perror("pread last"); + memcpy(tmpBlock, ((char*)buf) + nbytes - nbytesDiff, nbytesDiff); + if (write(fd, tmpBlock, sectorSize) == -1) + perror("write last"); + // repos at byte offset of latest wrote block + if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1) + perror("lseek2 last"); + } + + free(tmpBlock); + + return nbytes; + } + +} + + +ssize_t +haiku_freebsd_readv(int fd, const iovec *vecs, size_t count) +{ + ssize_t bytesRead = 0; + + for (size_t i = 0; i < count; i++) { + ssize_t currentRead = haiku_freebsd_read(fd, vecs[i].iov_base, + vecs[i].iov_len); + + if (currentRead < 0) + return bytesRead > 0 ? bytesRead : -1; + + bytesRead += currentRead; + + if ((size_t)currentRead != vecs[i].iov_len) + break; + } + + return bytesRead; +} + + +ssize_t +haiku_freebsd_writev(int fd, const struct iovec *vecs, size_t count) +{ + ssize_t bytesWritten = 0; + + for (size_t i = 0; i < count; i++) { + ssize_t written = haiku_freebsd_write(fd, vecs[i].iov_base, + vecs[i].iov_len); + + if (written < 0) + return bytesWritten > 0 ? bytesWritten : -1; + + bytesWritten += written; + + if ((size_t)written != vecs[i].iov_len) + break; + } + + return bytesWritten; +} diff --git a/src/build/libroot/fs_freebsd.h b/src/build/libroot/fs_freebsd.h new file mode 100644 index 0000000000..dd94cffa80 --- /dev/null +++ b/src/build/libroot/fs_freebsd.h @@ -0,0 +1,17 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef FS_FREEBSD_H +#define FS_FREEBSD_H + +#include + + +ssize_t haiku_freebsd_read(int fd, void *buf, size_t nbytes); +ssize_t haiku_freebsd_write(int fd, const void *buf, size_t nbytes); +ssize_t haiku_freebsd_readv(int fd, const iovec *vecs, size_t count); +ssize_t haiku_freebsd_writev(int fd, const struct iovec *vecs, size_t count); + + +#endif // FS_FREEBSD_H diff --git a/src/tools/fs_shell/uio.cpp b/src/tools/fs_shell/uio.cpp index 32fdd811e5..5f25d1908f 100644 --- a/src/tools/fs_shell/uio.cpp +++ b/src/tools/fs_shell/uio.cpp @@ -41,7 +41,11 @@ fssh_readv(int fd, const struct fssh_iovec *vector, fssh_size_t count) if (!prepare_iovecs(vector, count, systemVecs)) return -1; - return readv(fd, systemVecs, count); + #if !defined(HAIKU_HOST_PLATFORM_FREEBSD) + return readv(fd, systemVecs, count); + #else + return readv_pos(fd, lseek(fd, 0, SEEK_CUR), systemVecs, count); + #endif } @@ -64,7 +68,11 @@ fssh_writev(int fd, const struct fssh_iovec *vector, fssh_size_t count) if (!prepare_iovecs(vector, count, systemVecs)) return -1; - return writev(fd, systemVecs, count); + #if !defined(HAIKU_HOST_PLATFORM_FREEBSD) + return writev(fd, systemVecs, count); + #else + return writev_pos(fd, lseek(fd, 0, SEEK_CUR), systemVecs, count); + #endif } diff --git a/src/tools/fs_shell/unistd.cpp b/src/tools/fs_shell/unistd.cpp index b77af0fe6b..49f059611a 100644 --- a/src/tools/fs_shell/unistd.cpp +++ b/src/tools/fs_shell/unistd.cpp @@ -22,6 +22,9 @@ # if defined(HAIKU_HOST_PLATFORM_FREEBSD) \ || defined(HAIKU_HOST_PLATFORM_DARWIN) # include +# include +# include +# include # else // the (POSIX) correct place of definition for ioctl() # include @@ -195,9 +198,56 @@ fssh_ioctl(int fd, unsigned long op, ...) } else error = errno; + #elif HAIKU_HOST_PLATFORM_FREEBSD + { + // FreeBSD has not block devices + + struct stat status; + + if (fstat(fd, &status) == 0) { + struct disklabel disklabel; + off_t mediaSize; + + memset(&disklabel,0,sizeof disklabel); + + // Ignore errors, this way we can use memory devices (md%d) + ioctl(fd, DIOCGSECTORSIZE, &disklabel.d_secsize); + ioctl(fd, DIOCGFWSECTORS, &disklabel.d_nsectors); + ioctl(fd, DIOCGFWHEADS, &disklabel.d_ntracks); + ioctl(fd, DIOCGMEDIASIZE, &mediaSize); + + if (disklabel.d_nsectors == 0) { + // Seems to be a md device, then ioctls returns lots of + // zeroes and hardcode some defaults + disklabel.d_nsectors = 64; + disklabel.d_ntracks = 16; + } + + disklabel.d_secperunit = mediaSize / disklabel.d_secsize; + disklabel.d_ncylinders = mediaSize / disklabel.d_secsize + / disklabel.d_nsectors + / disklabel.d_ntracks; + + geometry->head_count = disklabel.d_ntracks; + geometry->cylinder_count = disklabel.d_ncylinders; + geometry->sectors_per_track = disklabel.d_nsectors; + + geometry->bytes_per_sector = disklabel.d_secsize; + // FreeBSD supports device_type flag as disklabel.d_type, + // for now we harcod it to B_DISK. + geometry->device_type = FSSH_B_DISK; + geometry->removable = disklabel.d_flags & D_REMOVABLE > 0; + // read_only? + geometry->read_only = false; + // FreeBSD does not support write_once flag. + geometry->write_once = false; + error = B_OK; + } else + error = errno; + } #else // Not implemented for this platform, i.e. we won't be able to - // deal with block devices. + // deal with disk devices. #endif break; @@ -245,7 +295,11 @@ fssh_ioctl(int fd, unsigned long op, ...) fssh_ssize_t fssh_read(int fd, void *buffer, fssh_size_t count) { - return read(fd, buffer, count); + #if !defined(HAIKU_HOST_PLATFORM_FREEBSD) + return read(fd, buffer, count); + #else + return read_pos(fd, lseek(fd, 0, SEEK_CUR), buffer, count); + #endif } @@ -259,7 +313,11 @@ fssh_read_pos(int fd, fssh_off_t pos, void *buffer, fssh_size_t count) fssh_ssize_t fssh_write(int fd, const void *buffer, fssh_size_t count) { - return write(fd, buffer, count); + #if !defined(HAIKU_HOST_PLATFORM_FREEBSD) + return write(fd, buffer, count); + #else + return write_pos(fd, lseek(fd, 0, SEEK_CUR), buffer, count); + #endif }