xref: /haiku/src/build/libroot/fs_freebsd.cpp (revision 1fe24d0cd0b547a771c00f6fca8f50ba6ca2fb2c)
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 #include "fs_freebsd.h"
8 
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/disk.h>
15 #include <sys/ioctl.h>
16 #include <sys/stat.h>
17 #include <sys/time.h>
18 #include <unistd.h>
19 
20 
21 // Read and write operations in FreeBSD only work on devices block by block.
22 
23 ssize_t
24 haiku_freebsd_read(int fd, void *buf, size_t nbytes)
25 {
26 	struct stat st;
27 	if (fstat(fd, &st) != 0)
28 		return -1;
29 
30 	if (S_ISREG(st.st_mode))
31 		return read(fd, buf, nbytes); // Is a file! Good :)
32 
33 	int sectorSize;
34 	if (ioctl(fd, DIOCGSECTORSIZE, &sectorSize) == -1)
35 		sectorSize = 512; // If fail, hardcode to 512 for now
36 
37 	off_t cur = lseek(fd, 0, SEEK_CUR);
38 	if (cur == -1)
39 		perror("lseek 1");
40 
41 	off_t seekDiff = (sectorSize - (cur % sectorSize)) % sectorSize;
42 	off_t nbytesDiff = (nbytes - seekDiff) % sectorSize;
43 
44 	if (seekDiff == 0 && nbytesDiff == 0) {
45 		// Not needed but this saves malloc and free operations
46 		return read(fd, buf, nbytes);
47 
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 		if (read(fd, tmpBlock, sectorSize) == -1)
57 			perror("read oneblock");
58 		memcpy((char*)buf, tmpBlock + cur % sectorSize, nbytes);
59 		// repos at byte offset of latest wrote block
60 		if (lseek(fd, -sectorSize + (cur % sectorSize) + nbytes, SEEK_CUR)
61 				== -1) {
62 			perror("lseek2 oneblock");
63 		}
64 
65 		free(tmpBlock);
66 
67 		return nbytes;
68 
69 	} else {
70 		// Needs to write more than a block
71 
72 		char* tmpBlock = (char*)malloc(sectorSize);
73 
74 		// First block if seek isn't
75 		if (seekDiff > 0) {
76 			// read entire block at 0 pos
77 			if (lseek(fd, -(sectorSize - seekDiff), SEEK_CUR) == -1)
78 				perror("lseek seekDiff");
79 			off_t sdCur = lseek(fd,0,SEEK_CUR);
80 			if (sdCur == -1)
81 				perror("lseek2 seekDiff");
82 			if (read(fd, tmpBlock, sectorSize) == -1 )
83 				perror("read seekDiff");
84 			// alter content
85 			memcpy(buf, tmpBlock + (sectorSize - seekDiff), seekDiff);
86 
87 		}
88 
89 		// Blocks between
90 		if ((nbytes - seekDiff) >= sectorSize) {
91 			if (read(fd, ((char*)buf) + seekDiff, nbytes - seekDiff - nbytesDiff) == -1)
92 				perror("read between");
93 		}
94 
95 		// Last block if overflow
96 		if (nbytesDiff > 0 ) {
97 
98 			off_t sdCur = lseek(fd, 0, SEEK_CUR);
99 			if (sdCur == -1)
100 				perror("lseek last");
101 			if (read(fd, tmpBlock, sectorSize) == -1)
102 				perror("read last");
103 			memcpy(((char*)buf) + nbytes - nbytesDiff, tmpBlock, nbytesDiff);
104 			// repos at byte offset of latest wrote block
105 			if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1)
106 				perror("lseek2 last");
107 		}
108 
109 		free(tmpBlock);
110 
111 		return nbytes;
112 	}
113 
114 }
115 
116 
117 ssize_t
118 haiku_freebsd_write(int fd, const void *buf, size_t nbytes)
119 {
120 	struct stat st;
121 	if (fstat(fd, &st) != 0)
122 		return -1;
123 
124 	if (S_ISREG(st.st_mode))
125 		return write(fd, buf, nbytes); // Is a file! Good :)
126 
127 	int sectorSize;
128 	if (ioctl(fd, DIOCGSECTORSIZE, &sectorSize) == -1)
129 		sectorSize = 512; // If fail, hardcode do 512 for now
130 
131 	off_t cur = lseek(fd, 0, SEEK_CUR);
132 	if (cur == -1)
133 		perror("lseek 1");
134 
135 	off_t seekDiff = (sectorSize - (cur % sectorSize)) % sectorSize;
136 	off_t nbytesDiff = (nbytes - seekDiff) % sectorSize;
137 
138 	if (seekDiff == 0 && nbytesDiff == 0) {
139 		// Not needed but this saves malloc and free operations
140 		return write(fd, buf, nbytes);
141 
142 	} else if (cur % sectorSize + nbytes <= sectorSize) {
143 		// Write complete in only a block
144 		char* tmpBlock = (char*)malloc(sectorSize);
145 
146 		// Put at start of the block
147 		off_t sdCur = lseek(fd, -(cur % sectorSize), SEEK_CUR);
148 		if (sdCur == -1)
149 			perror("lseek oneblock");
150 		if (pread(fd, tmpBlock, sectorSize, sdCur) == -1)
151 			perror("pread oneblock");
152 		memcpy(tmpBlock + cur % sectorSize, (char*)buf, nbytes);
153 		if (write(fd, tmpBlock, sectorSize) == -1)
154 			perror("write oneblock");
155 		// repos at byte offset of latest written block
156 		if (lseek(fd, -sectorSize + (cur % sectorSize) + nbytes, SEEK_CUR) == -1)
157 			perror("lseek2 oneblock");
158 
159 		free(tmpBlock);
160 
161 		return nbytes;
162 
163 	} else {
164 		// Needs to write more than a block
165 
166 		char* tmpBlock = (char*)malloc(sectorSize);
167 
168 		// First block if seek isn't
169 		if (seekDiff > 0) {
170 			// read entire block at 0 pos
171 			if (lseek(fd, -(sectorSize - seekDiff), SEEK_CUR) == -1)
172 				perror("lseek seekDiff");
173 			off_t sdCur = lseek(fd, 0, SEEK_CUR);
174 			if (sdCur == -1)
175 				perror("lseek2 seekDiff");
176 			if (pread(fd, tmpBlock, sectorSize, sdCur) == -1 )
177 				perror("pread seekDiff");
178 			// alter content
179 			memcpy(tmpBlock + (sectorSize - seekDiff), buf, seekDiff);
180 			if (write(fd, tmpBlock, sectorSize)==-1)
181 				perror("write seekDiff");
182 
183 		}
184 
185 		// Blocks between
186 		if ((nbytes - seekDiff) >= sectorSize) {
187 			if (write(fd, ((char*)buf) + seekDiff, nbytes - seekDiff - nbytesDiff) == -1)
188 				perror("write between");
189 		}
190 
191 		// Last block if overflow
192 		if (nbytesDiff > 0) {
193 
194 			off_t sdCur = lseek(fd, 0, SEEK_CUR);
195 			if (sdCur == -1)
196 				perror("lseek last");
197 			if (pread(fd, tmpBlock, sectorSize, sdCur) == -1)
198 				perror("pread last");
199 			memcpy(tmpBlock, ((char*)buf) + nbytes - nbytesDiff, nbytesDiff);
200 			if (write(fd, tmpBlock, sectorSize) == -1)
201 				perror("write last");
202 			// repos at byte offset of latest wrote block
203 			if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1)
204 				perror("lseek2 last");
205 		}
206 
207 		free(tmpBlock);
208 
209 		return nbytes;
210 	}
211 
212 }
213 
214 
215 ssize_t
216 haiku_freebsd_readv(int fd, const iovec *vecs, size_t count)
217 {
218 	ssize_t bytesRead = 0;
219 
220 	for (size_t i = 0; i < count; i++) {
221 		ssize_t currentRead = haiku_freebsd_read(fd, vecs[i].iov_base,
222 			vecs[i].iov_len);
223 
224 		if (currentRead < 0)
225 			return bytesRead > 0 ? bytesRead : -1;
226 
227 		bytesRead += currentRead;
228 
229 		if ((size_t)currentRead != vecs[i].iov_len)
230 			break;
231 	}
232 
233 	return bytesRead;
234 }
235 
236 
237 ssize_t
238 haiku_freebsd_writev(int fd, const struct iovec *vecs, size_t count)
239 {
240 	ssize_t bytesWritten = 0;
241 
242 	for (size_t i = 0; i < count; i++) {
243 		ssize_t written = haiku_freebsd_write(fd, vecs[i].iov_base,
244 			vecs[i].iov_len);
245 
246 		if (written < 0)
247 			return bytesWritten > 0 ? bytesWritten : -1;
248 
249 		bytesWritten += written;
250 
251 		if ((size_t)written != vecs[i].iov_len)
252 			break;
253 	}
254 
255 	return bytesWritten;
256 }
257 
258 
259 #if defined(_HAIKU_BUILD_NO_FUTIMENS) || defined(_HAIKU_BUILD_NO_FUTIMENS)
260 
261 template<typename File>
262 static int
263 utimes_helper(File& file, const struct timespec times[2])
264 {
265 	if (times == NULL)
266 		return file.SetTimes(NULL);
267 
268 	timeval timeBuffer[2];
269 	timeBuffer[0].tv_sec = times[0].tv_sec;
270 	timeBuffer[0].tv_usec = times[0].tv_nsec / 1000;
271 	timeBuffer[1].tv_sec = times[1].tv_sec;
272 	timeBuffer[1].tv_usec = times[1].tv_nsec / 1000;
273 
274 	if (times[0].tv_nsec == UTIME_OMIT || times[1].tv_nsec == UTIME_OMIT) {
275 		struct stat st;
276 		if (file.GetStat(st) != 0)
277 			return -1;
278 
279 		if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
280 			return 0;
281 
282 		if (times[0].tv_nsec == UTIME_OMIT) {
283 			timeBuffer[0].tv_sec = st.st_atimespec.tv_sec;
284 			timeBuffer[0].tv_usec = st.st_atimespec.tv_nsec / 1000;
285 		}
286 
287 		if (times[1].tv_nsec == UTIME_OMIT) {
288 			timeBuffer[1].tv_sec = st.st_mtimespec.tv_sec;
289 			timeBuffer[1].tv_usec = st.st_mtimespec.tv_nsec / 1000;
290 		}
291 	}
292 
293 	if (times[0].tv_nsec == UTIME_NOW || times[1].tv_nsec == UTIME_NOW) {
294 		timeval now;
295 		gettimeofday(&now, NULL);
296 
297 		if (times[0].tv_nsec == UTIME_NOW)
298 			timeBuffer[0] = now;
299 
300 		if (times[1].tv_nsec == UTIME_NOW)
301 			timeBuffer[1] = now;
302 	}
303 
304 	return file.SetTimes(timeBuffer);
305 }
306 
307 #endif	// _HAIKU_BUILD_NO_FUTIMENS || _HAIKU_BUILD_NO_FUTIMENS
308 
309 
310 #ifdef _HAIKU_BUILD_NO_FUTIMENS
311 
312 struct FDFile {
313 	FDFile(int fd)
314 		:
315 		fFD(fd)
316 	{
317 	}
318 
319 	int GetStat(struct stat& _st)
320 	{
321 		return fstat(fFD, &_st);
322 	}
323 
324 	int SetTimes(const timeval times[2])
325 	{
326 		return futimes(fFD, times);
327 	}
328 
329 private:
330 	int fFD;
331 };
332 
333 
334 int
335 futimens(int fd, const struct timespec times[2])
336 {
337 	FDFile file(fd);
338 	return utimes_helper(file, times);
339 }
340 
341 #endif	// _HAIKU_BUILD_NO_FUTIMENS
342 
343 
344 #ifdef _HAIKU_BUILD_NO_UTIMENSAT
345 
346 struct FDPathFile {
347 	FDPathFile(int fd, const char* path, int flag)
348 		:
349 		fFD(fd),
350 		fPath(path),
351 		fFlag(flag)
352 	{
353 	}
354 
355 	int GetStat(struct stat& _st)
356 	{
357 		return fstatat(fFD, fPath, &_st, fFlag);
358 	}
359 
360 	int SetTimes(const timeval times[2])
361 	{
362 		// TODO: fFlag (AT_SYMLINK_NOFOLLOW) is not supported here!
363 		return futimesat(fFD, fPath, times);
364 	}
365 
366 private:
367 	int			fFD;
368 	const char*	fPath;
369 	int			fFlag;
370 };
371 
372 
373 int
374 utimensat(int fd, const char* path, const struct timespec times[2], int flag)
375 {
376 	FDPathFile file(fd, path, flag);
377 	return utimes_helper(file, times);
378 }
379 
380 #endif	// _HAIKU_BUILD_NO_UTIMENSAT
381