* Copyright 2008, Samuel Rodriguez Perez, samuelgaliza@gmail.com.
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "fs_freebsd.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/disk.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
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);
uint sectorSize;
if (ioctl(fd, DIOCGSECTORSIZE, §orSize) == -1)
sectorSize = 512;
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) {
return read(fd, buf, nbytes);
} else if (cur % sectorSize + nbytes <= sectorSize) {
char* tmpBlock = (char*)malloc(sectorSize);
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);
if (lseek(fd, -sectorSize + (cur % sectorSize) + nbytes, SEEK_CUR)
== -1) {
perror("lseek2 oneblock");
}
free(tmpBlock);
return nbytes;
} else {
char* tmpBlock = (char*)malloc(sectorSize);
if (seekDiff > 0) {
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");
memcpy(buf, tmpBlock + (sectorSize - seekDiff), seekDiff);
}
if ((nbytes - seekDiff) >= sectorSize) {
if (read(fd, ((char*)buf) + seekDiff, nbytes - seekDiff
- nbytesDiff) == -1) {
perror("read between");
}
}
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);
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);
uint sectorSize;
if (ioctl(fd, DIOCGSECTORSIZE, §orSize) == -1)
sectorSize = 512;
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) {
return write(fd, buf, nbytes);
} else if (cur % sectorSize + nbytes <= sectorSize) {
char* tmpBlock = (char*)malloc(sectorSize);
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");
if (lseek(fd, -sectorSize + (cur % sectorSize) + nbytes, SEEK_CUR)
== -1) {
perror("lseek2 oneblock");
}
free(tmpBlock);
return nbytes;
} else {
char* tmpBlock = (char*)malloc(sectorSize);
if (seekDiff > 0) {
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");
memcpy(tmpBlock + (sectorSize - seekDiff), buf, seekDiff);
if (write(fd, tmpBlock, sectorSize) == -1)
perror("write seekDiff");
}
if ((nbytes - seekDiff) >= sectorSize) {
if (write(fd, ((char*)buf) + seekDiff, nbytes - seekDiff
- nbytesDiff) == -1) {
perror("write between");
}
}
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");
if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1)
perror("lseek2 last");
}
free(tmpBlock);
return nbytes;
}
}
ssize_t
haiku_freebsd_readv(int fd, const struct 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;
}