vfs: check userland buffers before reading them.
[haiku.git] / src / build / libroot / fs_freebsd.cpp
blob19acdcb704638aaa0577ebafda824afe7413c1ce
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 */
8 #include "fs_freebsd.h"
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>
22 // Read and write operations in FreeBSD only work on devices block by block.
24 ssize_t
25 haiku_freebsd_read(int fd, void *buf, size_t nbytes)
27 struct stat st;
28 if (fstat(fd, &st) != 0)
29 return -1;
31 if (S_ISREG(st.st_mode))
32 return read(fd, buf, nbytes); // Is a file! Good :)
34 uint sectorSize;
35 if (ioctl(fd, DIOCGSECTORSIZE, &sectorSize) == -1)
36 sectorSize = 512; // If fail, hardcode to 512 for now
38 off_t cur = lseek(fd, 0, SEEK_CUR);
39 if (cur == -1)
40 perror("lseek 1");
42 off_t seekDiff = (sectorSize - (cur % sectorSize)) % sectorSize;
43 off_t nbytesDiff = (nbytes - seekDiff) % sectorSize;
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);
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");
57 if (read(fd, tmpBlock, sectorSize) == -1)
58 perror("read oneblock");
60 memcpy((char*)buf, tmpBlock + cur % sectorSize, nbytes);
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");
68 free(tmpBlock);
70 return nbytes;
71 } else {
72 // Needs to write more than a block
74 char* tmpBlock = (char*)malloc(sectorSize);
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");
82 off_t sdCur = lseek(fd,0,SEEK_CUR);
83 if (sdCur == -1)
84 perror("lseek2 seekDiff");
86 if (read(fd, tmpBlock, sectorSize) == -1 )
87 perror("read seekDiff");
89 // alter content
90 memcpy(buf, tmpBlock + (sectorSize - seekDiff), seekDiff);
93 // Blocks between
94 if ((nbytes - seekDiff) >= sectorSize) {
95 if (read(fd, ((char*)buf) + seekDiff, nbytes - seekDiff
96 - nbytesDiff) == -1) {
97 perror("read between");
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");
107 if (read(fd, tmpBlock, sectorSize) == -1)
108 perror("read last");
110 memcpy(((char*)buf) + nbytes - nbytesDiff, tmpBlock, nbytesDiff);
112 // repos at byte offset of latest wrote block
113 if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1)
114 perror("lseek2 last");
117 free(tmpBlock);
119 return nbytes;
124 ssize_t
125 haiku_freebsd_write(int fd, const void *buf, size_t nbytes)
127 struct stat st;
128 if (fstat(fd, &st) != 0)
129 return -1;
131 if (S_ISREG(st.st_mode))
132 return write(fd, buf, nbytes); // Is a file! Good :)
134 uint sectorSize;
135 if (ioctl(fd, DIOCGSECTORSIZE, &sectorSize) == -1)
136 sectorSize = 512; // If fail, hardcode do 512 for now
138 off_t cur = lseek(fd, 0, SEEK_CUR);
139 if (cur == -1)
140 perror("lseek 1");
142 off_t seekDiff = (sectorSize - (cur % sectorSize)) % sectorSize;
143 off_t nbytesDiff = (nbytes - seekDiff) % sectorSize;
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);
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");
157 if (pread(fd, tmpBlock, sectorSize, sdCur) == -1)
158 perror("pread oneblock");
160 memcpy(tmpBlock + cur % sectorSize, (char*)buf, nbytes);
161 if (write(fd, tmpBlock, sectorSize) == -1)
162 perror("write oneblock");
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");
170 free(tmpBlock);
172 return nbytes;
173 } else {
174 // Needs to write more than a block
176 char* tmpBlock = (char*)malloc(sectorSize);
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");
184 off_t sdCur = lseek(fd, 0, SEEK_CUR);
185 if (sdCur == -1)
186 perror("lseek2 seekDiff");
188 if (pread(fd, tmpBlock, sectorSize, sdCur) == -1)
189 perror("pread seekDiff");
191 // alter content
192 memcpy(tmpBlock + (sectorSize - seekDiff), buf, seekDiff);
193 if (write(fd, tmpBlock, sectorSize) == -1)
194 perror("write seekDiff");
197 // Blocks between
198 if ((nbytes - seekDiff) >= sectorSize) {
199 if (write(fd, ((char*)buf) + seekDiff, nbytes - seekDiff
200 - nbytesDiff) == -1) {
201 perror("write between");
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");
211 if (pread(fd, tmpBlock, sectorSize, sdCur) == -1)
212 perror("pread last");
214 memcpy(tmpBlock, ((char*)buf) + nbytes - nbytesDiff, nbytesDiff);
215 if (write(fd, tmpBlock, sectorSize) == -1)
216 perror("write last");
218 // repos at byte offset of latest wrote block
219 if (lseek(fd, -(sectorSize - nbytesDiff), SEEK_CUR) == -1)
220 perror("lseek2 last");
223 free(tmpBlock);
225 return nbytes;
230 ssize_t
231 haiku_freebsd_readv(int fd, const struct iovec *vecs, size_t count)
233 ssize_t bytesRead = 0;
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);
239 if (currentRead < 0)
240 return bytesRead > 0 ? bytesRead : -1;
242 bytesRead += currentRead;
244 if ((size_t)currentRead != vecs[i].iov_len)
245 break;
248 return bytesRead;
252 ssize_t
253 haiku_freebsd_writev(int fd, const struct iovec *vecs, size_t count)
255 ssize_t bytesWritten = 0;
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);
261 if (written < 0)
262 return bytesWritten > 0 ? bytesWritten : -1;
264 bytesWritten += written;
266 if ((size_t)written != vecs[i].iov_len)
267 break;
270 return bytesWritten;