xref: /haiku/src/build/libroot/fs_freebsd.cpp (revision 95c9effd68127df2dce202d5e254a7c86560010a)
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 
24 ssize_t
25 haiku_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 
124 ssize_t
125 haiku_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 
230 ssize_t
231 haiku_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 
252 ssize_t
253 haiku_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