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
This commit is contained in:
Ingo Weinhold 2008-02-05 20:54:42 +00:00
parent 0d83dc06e3
commit 80b2da5e53
7 changed files with 476 additions and 15 deletions

View File

@ -19,7 +19,7 @@
#include <Resources.h>
#include <TypeConstants.h>
// Linux support
// Linux and FreeBSD support
#ifdef HAIKU_HOST_PLATFORM_LINUX
# include <ctype.h>
# include <linux/hdreg.h>
@ -27,7 +27,15 @@
# include "PartitionMap.h"
# include "PartitionMapParser.h"
#endif // HAIKU_HOST_PLATFORM_LINUX
#elif HAIKU_HOST_PLATFORM_FREEBSD
# include <ctype.h>
# include <sys/disklabel.h>
# include <sys/disk.h>
# include <sys/ioctl.h>
# 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

View File

@ -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

View File

@ -1,3 +1,7 @@
/*
* Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <BeOSBuildCompatibility.h>
@ -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;

View File

@ -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 <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
// 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, &sectorSize) == -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, &sectorSize) == -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;
}

View File

@ -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 <sys/uio.h>
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

View File

@ -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
}

View File

@ -22,6 +22,9 @@
# if defined(HAIKU_HOST_PLATFORM_FREEBSD) \
|| defined(HAIKU_HOST_PLATFORM_DARWIN)
# include <sys/ioctl.h>
# include <sys/stat.h>
# include <sys/disk.h>
# include <sys/disklabel.h>
# else
// the (POSIX) correct place of definition for ioctl()
# include <stropts.h>
@ -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
}