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, §orSize) == -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, §orSize) == -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