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