1/*
2 * Copyright 2008, Samuel Rodriguez Perez, samuelgaliza@gmail.com.
3 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "fs_freebsd.h"
9
10#include <errno.h>
11#include <fcntl.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/disk.h>
16#include <sys/ioctl.h>
17#include <sys/stat.h>
18#include <sys/time.h>
19#include <unistd.h>
20
21
22// Read and write operations in FreeBSD only work on devices block by block.
23
24ssize_t
25haiku_freebsd_read(int fd, void *buf, size_t nbytes)
26{
27	struct stat st;
28	if (fstat(fd, &st) != 0)
29		return -1;
30
31	if (S_ISREG(st.st_mode))
32		return read(fd, buf, nbytes); // Is a file! Good :)
33
34	uint sectorSize;
35	if (ioctl(fd, DIOCGSECTORSIZE, &sectorSize) == -1)
36		sectorSize = 512; // If fail, hardcode to 512 for now
37
38	off_t cur = lseek(fd, 0, SEEK_CUR);
39	if (cur == -1)
40		perror("lseek 1");
41
42	off_t seekDiff = (sectorSize - (cur % sectorSize)) % sectorSize;
43	off_t nbytesDiff = (nbytes - seekDiff) % sectorSize;
44
45	if (seekDiff == 0 && nbytesDiff == 0) {
46		// Not needed but this saves malloc and free operations
47		return read(fd, buf, nbytes);
48	} else if (cur % sectorSize + nbytes <= sectorSize) {
49		// Read complete in only a block
50		char* tmpBlock = (char*)malloc(sectorSize);
51
52		// Put at start of the block
53		off_t sdCur = lseek(fd, -(cur % sectorSize), SEEK_CUR);
54		if (sdCur == -1)
55			perror("lseek oneblock");
56
57		if (read(fd, tmpBlock, sectorSize) == -1)
58			perror("read oneblock");
59
60		memcpy((char*)buf, tmpBlock + cur % sectorSize, nbytes);
61
62		// repos at byte offset of latest wrote block
63		if (lseek(fd, -sectorSize + (cur % sectorSize) + nbytes, SEEK_CUR)
64				== -1) {
65			perror("lseek2 oneblock");
66		}
67
68		free(tmpBlock);
69
70		return nbytes;
71	} else {
72		// Needs to write more than a block
73
74		char* tmpBlock = (char*)malloc(sectorSize);
75
76		// First block if seek isn't
77		if (seekDiff > 0) {
78			// read entire block at 0 pos
79			if (lseek(fd, -(sectorSize - seekDiff), SEEK_CUR) == -1)
80				perror("lseek seekDiff");
81
82			off_t sdCur = lseek(fd,0,SEEK_CUR);
83			if (sdCur == -1)
84				perror("lseek2 seekDiff");
85
86			if (read(fd, tmpBlock, sectorSize) == -1 )
87				perror("read seekDiff");
88
89			// alter content
90			memcpy(buf, tmpBlock + (sectorSize - seekDiff), seekDiff);
91		}
92
93		// Blocks between
94		if ((nbytes - seekDiff) >= sectorSize) {
95			if (read(fd, ((char*)buf) + seekDiff, nbytes - seekDiff
96					- nbytesDiff) == -1) {
97				perror("read between");
98			}
99		}
100
101		// Last block if overflow
102		if (nbytesDiff > 0 ) {
103			off_t sdCur = lseek(fd, 0, SEEK_CUR);
104			if (sdCur == -1)
105				perror("lseek last");
106
107			if (read(fd, tmpBlock, sectorSize) == -1)
108				perror("read last");
109
110			memcpy(((char*)buf) + nbytes - nbytesDiff, tmpBlock, nbytesDiff);
111
112			// repos at byte offset of latest wrote block
113			if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1)
114				perror("lseek2 last");
115		}
116
117		free(tmpBlock);
118
119		return nbytes;
120	}
121}
122
123
124ssize_t
125haiku_freebsd_write(int fd, const void *buf, size_t nbytes)
126{
127	struct stat st;
128	if (fstat(fd, &st) != 0)
129		return -1;
130
131	if (S_ISREG(st.st_mode))
132		return write(fd, buf, nbytes); // Is a file! Good :)
133
134	uint sectorSize;
135	if (ioctl(fd, DIOCGSECTORSIZE, &sectorSize) == -1)
136		sectorSize = 512; // If fail, hardcode do 512 for now
137
138	off_t cur = lseek(fd, 0, SEEK_CUR);
139	if (cur == -1)
140		perror("lseek 1");
141
142	off_t seekDiff = (sectorSize - (cur % sectorSize)) % sectorSize;
143	off_t nbytesDiff = (nbytes - seekDiff) % sectorSize;
144
145	if (seekDiff == 0 && nbytesDiff == 0) {
146		// Not needed but this saves malloc and free operations
147		return write(fd, buf, nbytes);
148	} else if (cur % sectorSize + nbytes <= sectorSize) {
149		// Write complete in only a block
150		char* tmpBlock = (char*)malloc(sectorSize);
151
152		// Put at start of the block
153		off_t sdCur = lseek(fd, -(cur % sectorSize), SEEK_CUR);
154		if (sdCur == -1)
155			perror("lseek oneblock");
156
157		if (pread(fd, tmpBlock, sectorSize, sdCur) == -1)
158			perror("pread oneblock");
159
160		memcpy(tmpBlock + cur % sectorSize, (char*)buf, nbytes);
161		if (write(fd, tmpBlock, sectorSize) == -1)
162			perror("write oneblock");
163
164		// repos at byte offset of latest written block
165		if (lseek(fd, -sectorSize + (cur % sectorSize) + nbytes, SEEK_CUR)
166				== -1) {
167			perror("lseek2 oneblock");
168		}
169
170		free(tmpBlock);
171
172		return nbytes;
173	} else {
174		// Needs to write more than a block
175
176		char* tmpBlock = (char*)malloc(sectorSize);
177
178		// First block if seek isn't
179		if (seekDiff > 0) {
180			// read entire block at 0 pos
181			if (lseek(fd, -(sectorSize - seekDiff), SEEK_CUR) == -1)
182				perror("lseek seekDiff");
183
184			off_t sdCur = lseek(fd, 0, SEEK_CUR);
185			if (sdCur == -1)
186				perror("lseek2 seekDiff");
187
188			if (pread(fd, tmpBlock, sectorSize, sdCur) == -1)
189				perror("pread seekDiff");
190
191			// alter content
192			memcpy(tmpBlock + (sectorSize - seekDiff), buf, seekDiff);
193			if (write(fd, tmpBlock, sectorSize) == -1)
194				perror("write seekDiff");
195		}
196
197		// Blocks between
198		if ((nbytes - seekDiff) >= sectorSize) {
199			if (write(fd, ((char*)buf) + seekDiff, nbytes - seekDiff
200					- nbytesDiff) == -1) {
201				perror("write between");
202			}
203		}
204
205		// Last block if overflow
206		if (nbytesDiff > 0) {
207			off_t sdCur = lseek(fd, 0, SEEK_CUR);
208			if (sdCur == -1)
209				perror("lseek last");
210
211			if (pread(fd, tmpBlock, sectorSize, sdCur) == -1)
212				perror("pread last");
213
214			memcpy(tmpBlock, ((char*)buf) + nbytes - nbytesDiff, nbytesDiff);
215			if (write(fd, tmpBlock, sectorSize) == -1)
216				perror("write last");
217
218			// repos at byte offset of latest wrote block
219			if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1)
220				perror("lseek2 last");
221		}
222
223		free(tmpBlock);
224
225		return nbytes;
226	}
227}
228
229
230ssize_t
231haiku_freebsd_readv(int fd, const struct iovec *vecs, size_t count)
232{
233	ssize_t bytesRead = 0;
234
235	for (size_t i = 0; i < count; i++) {
236		ssize_t currentRead = haiku_freebsd_read(fd, vecs[i].iov_base,
237			vecs[i].iov_len);
238
239		if (currentRead < 0)
240			return bytesRead > 0 ? bytesRead : -1;
241
242		bytesRead += currentRead;
243
244		if ((size_t)currentRead != vecs[i].iov_len)
245			break;
246	}
247
248	return bytesRead;
249}
250
251
252ssize_t
253haiku_freebsd_writev(int fd, const struct iovec *vecs, size_t count)
254{
255	ssize_t bytesWritten = 0;
256
257	for (size_t i = 0; i < count; i++) {
258		ssize_t written = haiku_freebsd_write(fd, vecs[i].iov_base,
259			vecs[i].iov_len);
260
261		if (written < 0)
262			return bytesWritten > 0 ? bytesWritten : -1;
263
264		bytesWritten += written;
265
266		if ((size_t)written != vecs[i].iov_len)
267			break;
268	}
269
270	return bytesWritten;
271}
272