1/* 2 * Copyright (c) 2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* CFConcreteStreams.c 25 Copyright (c) 2000-2013, Apple Inc. All rights reserved. 26 Responsibility: John Iarocci 27*/ 28 29#include "CFStreamInternal.h" 30#include "CFInternal.h" 31#include <CoreFoundation/CFPriv.h> 32#include <CoreFoundation/CFNumber.h> 33#include <sys/types.h> 34#include <stdlib.h> 35#include <fcntl.h> 36#include <string.h> 37#include <stdio.h> 38#include <sys/stat.h> 39#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 40#include <sys/time.h> 41#include <unistd.h> 42#endif 43 44 45#define SCHEDULE_AFTER_WRITE (0) 46#define SCHEDULE_AFTER_READ (1) 47#define APPEND (3) 48#define AT_EOF (4) 49#define USE_RUNLOOP_ARRAY (5) 50 51 52/* File callbacks */ 53typedef struct { 54 CFURLRef url; 55 int fd; 56#ifdef REAL_FILE_SCHEDULING 57 union { 58 CFFileDescriptorRef cffd; // ref created once we open and have an fd 59 CFMutableArrayRef rlArray; // scheduling information prior to open 60 } rlInfo; // If fd > 0, cffd exists. Otherwise, rlArray. 61#else 62 uint16_t scheduled; // ref count of how many times we've been scheduled 63#endif 64 CFOptionFlags flags; 65 off_t offset; 66} _CFFileStreamContext; 67 68 69CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset, "kCFStreamPropertyFileCurrentOffset"); 70#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 71CONST_STRING_DECL(_kCFStreamPropertyFileNativeHandle, "_kCFStreamPropertyFileNativeHandle"); 72#endif 73 74#ifdef REAL_FILE_SCHEDULING 75extern void _CFFileDescriptorInduceFakeReadCallBack(CFFileDescriptorRef); 76static void fileCallBack(CFFileDescriptorRef f, CFOptionFlags callBackTypes, void *info); 77 78static void constructCFFD(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) { 79 CFFileDescriptorContext context = {0, stream, NULL, NULL, (void *)CFCopyDescription}; 80 CFFileDescriptorRef cffd = CFFileDescriptorCreate(CFGetAllocator(stream), fileStream->fd, false, fileCallBack, &context); 81 CFFileDescriptorEnableCallBacks(cffd, forRead ? kCFFileDescriptorReadCallBack : kCFFileDescriptorWriteCallBack); 82 if (fileStream->rlInfo.rlArray) { 83 CFIndex i, c = CFArrayGetCount(fileStream->rlInfo.rlArray); 84 CFRunLoopSourceRef src = CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream), cffd, 0); 85 for (i = 0; i+1 < c; i += 2) { 86 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i); 87 CFStringRef mode = CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i+1); 88 CFRunLoopAddSource(rl, src, mode); 89 } 90 CFRelease(fileStream->rlInfo.rlArray); 91 CFRelease(src); 92 } 93 fileStream->rlInfo.cffd = cffd; 94} 95#endif 96 97static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *error, Boolean forRead, struct _CFStream *stream) { 98 int flags = forRead ? O_RDONLY : (O_CREAT | O_TRUNC | O_WRONLY); 99#if DEPLOYMENT_TARGET_WINDOWS 100 wchar_t path[CFMaxPathSize]; 101 flags |= (_O_BINARY|_O_NOINHERIT); 102 if (_CFURLGetWideFileSystemRepresentation(fileStream->url, TRUE, path, CFMaxPathSize) == FALSE) 103#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 104 char path[CFMaxPathSize]; 105 if (CFURLGetFileSystemRepresentation(fileStream->url, TRUE, (UInt8 *)path, CFMaxPathSize) == FALSE) 106#endif 107 { 108 error->error = ENOENT; 109 error->domain = kCFStreamErrorDomainPOSIX; 110 return FALSE; 111 } 112 if (__CFBitIsSet(fileStream->flags, APPEND)) { 113 flags |= O_APPEND; 114 flags &= ~O_TRUNC; 115 } 116 117 do { 118#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 119 fileStream->fd = open((const char *)path, flags, 0666); 120#elif DEPLOYMENT_TARGET_WINDOWS 121 fileStream->fd = _wopen(path, flags, 0666); 122#endif 123 if (fileStream->fd < 0) 124 break; 125 126 if ((fileStream->offset != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) 127 break; 128 129#ifdef REAL_FILE_SCHEDULING 130 if (fileStream->rlInfo.rlArray != NULL) { 131 constructCFFD(fileStream, forRead, stream); 132 } 133#endif 134 135 return TRUE; 136 } while (1); 137 138 __CFBitSet(fileStream->flags, USE_RUNLOOP_ARRAY); 139 error->error = errno; 140 error->domain = kCFStreamErrorDomainPOSIX; 141 142 return FALSE; 143} 144 145static Boolean fileOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { 146 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; 147 Boolean forRead = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); 148 *openComplete = TRUE; 149 if (ctxt->url) { 150 if (constructFD(ctxt, errorCode, forRead, stream)) { 151#ifndef REAL_FILE_SCHEDULING 152 if (ctxt->scheduled > 0) { 153 if (forRead) 154 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); 155 else 156 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); 157 } 158#endif 159 return TRUE; 160 } else { 161 return FALSE; 162 } 163#ifdef REAL_FILE_SCHEDULING 164 } else if (ctxt->rlInfo.rlArray != NULL) { 165 constructCFFD(ctxt, forRead, stream); 166#endif 167 } 168 return TRUE; 169} 170 171CF_PRIVATE CFIndex fdRead(int fd, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF) { 172 CFIndex bytesRead = read(fd, buffer, bufferLength); 173 if (bytesRead < 0) { 174 errorCode->error = errno; 175 errorCode->domain = kCFStreamErrorDomainPOSIX; 176 return -1; 177 } else { 178 *atEOF = (bytesRead == 0) ? TRUE : FALSE; 179 errorCode->error = 0; 180 return bytesRead; 181 } 182} 183 184static CFIndex fileRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF, void *info) { 185 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; 186 CFIndex result; 187 result = fdRead(ctxt->fd, buffer, bufferLength, errorCode, atEOF); 188#ifdef REAL_FILE_SCHEDULING 189 if (__CFBitIsSet(ctxt->flags, SCHEDULE_AFTER_READ)) { 190 __CFBitClear(ctxt->flags, SCHEDULE_AFTER_READ); 191 if (!*atEOF && ctxt->rlInfo.cffd) { 192 struct stat statbuf; 193 int ret = fstat(ctxt->fd, &statbuf); 194 if (0 <= ret && (S_IFREG == (statbuf.st_mode & S_IFMT))) { 195 off_t offset = lseek(ctxt->fd, 0, SEEK_CUR); 196 if (statbuf.st_size == offset) { 197 _CFFileDescriptorInduceFakeReadCallBack(ctxt->rlInfo.cffd); 198 } 199 } 200 } 201 if (ctxt->rlInfo.cffd) { 202 CFFileDescriptorEnableCallBacks(ctxt->rlInfo.cffd, kCFFileDescriptorReadCallBack); 203 } 204 } 205#else 206 if (*atEOF) 207 __CFBitSet(ctxt->flags, AT_EOF); 208 if (ctxt->scheduled > 0 && !*atEOF) { 209 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); 210 } 211#endif 212 return result; 213} 214 215#ifdef REAL_FILE_SCHEDULING 216CF_PRIVATE Boolean fdCanRead(int fd) { 217 struct timeval timeout = {0, 0}; 218 fd_set *readSetPtr; 219 fd_set readSet; 220 Boolean result; 221// fd_set is not a mask in Win32, so checking for an fd that's too big is not relevant 222 if (fd < FD_SETSIZE) { 223 FD_ZERO(&readSet); 224 readSetPtr = &readSet; 225 } else { 226 int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t); 227 uint32_t *fds_bits = (uint32_t *)malloc(size); 228 memset(fds_bits, 0, size); 229 readSetPtr = (fd_set *)fds_bits; 230 } 231 FD_SET(fd, readSetPtr); 232 result = (select(fd + 1, readSetPtr, NULL, NULL, &timeout) == 1) ? TRUE : FALSE; 233 if (readSetPtr != &readSet) { 234 free(readSetPtr); 235 } 236 return result; 237} 238#endif 239 240static Boolean fileCanRead(CFReadStreamRef stream, void *info) { 241 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; 242#ifdef REAL_FILE_SCHEDULING 243 return fdCanRead(ctxt->fd); 244#else 245 return !__CFBitIsSet(ctxt->flags, AT_EOF); 246#endif 247} 248 249CF_PRIVATE CFIndex fdWrite(int fd, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode) { 250 CFIndex bytesWritten = write(fd, buffer, bufferLength); 251 if (bytesWritten < 0) { 252 errorCode->error = errno; 253 errorCode->domain = kCFStreamErrorDomainPOSIX; 254 return -1; 255 } else { 256 errorCode->error = 0; 257 return bytesWritten; 258 } 259} 260 261static CFIndex fileWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) { 262 _CFFileStreamContext *fileStream = ((_CFFileStreamContext *)info); 263 CFIndex result = fdWrite(fileStream->fd, buffer, bufferLength, errorCode); 264#ifdef REAL_FILE_SCHEDULING 265 if (__CFBitIsSet(fileStream->flags, SCHEDULE_AFTER_WRITE)) { 266 __CFBitClear(fileStream->flags, SCHEDULE_AFTER_WRITE); 267 if (fileStream->rlInfo.cffd) { 268 CFFileDescriptorEnableCallBacks(fileStream->rlInfo.cffd, kCFFileDescriptorWriteCallBack); 269 } 270 } 271#else 272 if (fileStream->scheduled > 0) { 273 CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL); 274 } 275#endif 276 return result; 277} 278 279#ifdef REAL_FILE_SCHEDULING 280CF_PRIVATE Boolean fdCanWrite(int fd) { 281 struct timeval timeout = {0, 0}; 282 fd_set *writeSetPtr; 283 fd_set writeSet; 284 Boolean result; 285 if (fd < FD_SETSIZE) { 286 FD_ZERO(&writeSet); 287 writeSetPtr = &writeSet; 288 } else { 289 int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t); 290 uint32_t *fds_bits = (uint32_t *)malloc(size); 291 memset(fds_bits, 0, size); 292 writeSetPtr = (fd_set *)fds_bits; 293 } 294 FD_SET(fd, writeSetPtr); 295 result = (select(fd + 1, NULL, writeSetPtr, NULL, &timeout) == 1) ? TRUE : FALSE; 296 if (writeSetPtr != &writeSet) { 297 free(writeSetPtr); 298 } 299 return result; 300} 301#endif 302 303static Boolean fileCanWrite(CFWriteStreamRef stream, void *info) { 304#ifdef REAL_FILE_SCHEDULING 305 return fdCanWrite(((_CFFileStreamContext *)info)->fd); 306#else 307 return TRUE; 308#endif 309} 310 311static void fileClose(struct _CFStream *stream, void *info) { 312 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; 313 if (ctxt->fd >= 0) { 314 close(ctxt->fd); 315 ctxt->fd = -1; 316#ifdef REAL_FILE_SCHEDULING 317 if (ctxt->rlInfo.cffd) { 318 CFFileDescriptorInvalidate(ctxt->rlInfo.cffd); 319 CFRelease(ctxt->rlInfo.cffd); 320 ctxt->rlInfo.cffd = NULL; 321 } 322 } else if (ctxt->rlInfo.rlArray) { 323 CFRelease(ctxt->rlInfo.rlArray); 324 ctxt->rlInfo.rlArray = NULL; 325#endif 326 } 327} 328 329#ifdef REAL_FILE_SCHEDULING 330static void fileCallBack(CFFileDescriptorRef f, CFOptionFlags type, void *info) { 331 struct _CFStream *stream = (struct _CFStream *)info; 332 Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); 333 _CFFileStreamContext *fileStream = isReadStream ? CFReadStreamGetInfoPointer((CFReadStreamRef)stream) : CFWriteStreamGetInfoPointer((CFWriteStreamRef)stream); 334 if (type == kCFFileDescriptorWriteCallBack) { 335 __CFBitSet(fileStream->flags, SCHEDULE_AFTER_WRITE); 336 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); 337 } else { 338 __CFBitSet(fileStream->flags, SCHEDULE_AFTER_READ); 339 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); 340 } 341} 342#endif 343 344static void fileSchedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) { 345 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; 346 Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); 347 CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream); 348 if (fileStream->fd < 0 && status != kCFStreamStatusNotOpen) { 349 // Stream's already closed or error-ed out 350 return; 351 } 352#ifdef REAL_FILE_SCHEDULING 353 if (status == kCFStreamStatusNotOpen) { 354 if (!fileStream->rlInfo.rlArray) { 355 fileStream->rlInfo.rlArray = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks); 356 } 357 CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoop); 358 CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoopMode); 359 } else { 360 CFRunLoopSourceRef rlSrc; 361 if (!fileStream->rlInfo.cffd) { 362 constructCFFD(fileStream, isReadStream, stream); 363 } 364 rlSrc = CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.cffd, 0); 365 CFRunLoopAddSource(runLoop, rlSrc, runLoopMode); 366 CFRelease(rlSrc); 367 } 368#else 369 fileStream->scheduled++; 370 if (fileStream->scheduled == 1 && fileStream->fd > 0 && status == kCFStreamStatusOpen) { 371 if (isReadStream) 372 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); 373 else 374 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); 375 } 376#endif 377} 378 379static void fileUnschedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) { 380 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; 381#ifdef REAL_FILE_SCHEDULING 382 Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); 383 CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream); 384 if (status == kCFStreamStatusNotOpen) { 385 // Not opened yet 386 if (fileStream->rlInfo.rlArray) { 387 CFMutableArrayRef runloops = fileStream->rlInfo.rlArray; 388 CFIndex i, c; 389 for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) { 390 if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) { 391 CFArrayRemoveValueAtIndex(runloops, i); 392 CFArrayRemoveValueAtIndex(runloops, i); 393 break; 394 } 395 } 396 } 397 } else if (fileStream->rlInfo.cffd) { 398 if (__CFBitIsSet(fileStream->flags, USE_RUNLOOP_ARRAY)) { 399 // we know that fileStream->rlInfo.rlArray is non-NULL because it is in a union with fileStream->rlInfo.cffd 400 CFMutableArrayRef runloops = fileStream->rlInfo.rlArray; 401 CFIndex i, c; 402 for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) { 403 if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) { 404 CFArrayRemoveValueAtIndex(runloops, i); 405 CFArrayRemoveValueAtIndex(runloops, i); 406 break; 407 } 408 } 409 } else { 410 CFRunLoopSourceRef rlSrc = CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.cffd, 0); 411 CFRunLoopRemoveSource(runLoop, rlSrc, runLoopMode); 412 CFRelease(rlSrc); 413 } 414 } 415#else 416 if (fileStream->scheduled > 0) 417 fileStream->scheduled--; 418#endif 419} 420 421static CFTypeRef fileCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) { 422 423 CFTypeRef result = NULL; 424 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; 425 426 if (CFEqual(propertyName, kCFStreamPropertyFileCurrentOffset)) { 427 428 // NOTE that this does a lseek of 0 from the current location in 429 // order to populate the offset field which will then be used to 430 // create the resulting value. 431 if (!__CFBitIsSet(fileStream->flags, APPEND) && fileStream->fd != -1) { 432 fileStream->offset = lseek(fileStream->fd, 0, SEEK_CUR); 433 } 434 435 if (fileStream->offset != -1) { 436 result = CFNumberCreate(CFGetAllocator((CFTypeRef)stream), kCFNumberSInt64Type, &(fileStream->offset)); 437 } 438#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 439 } else if (CFEqual(propertyName, _kCFStreamPropertyFileNativeHandle)) { 440 int fd = fileStream->fd; 441 if (fd != -1) { 442 result = CFDataCreate(CFGetAllocator((CFTypeRef) stream), (const uint8_t *)&fd, sizeof(fd)); 443 } 444#endif 445 } 446 447 return result; 448} 449 450static Boolean fileSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val, void *info) { 451 452 Boolean result = FALSE; 453 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; 454 455 if (CFEqual(prop, kCFStreamPropertyAppendToFile) && CFGetTypeID(stream) == CFWriteStreamGetTypeID() && 456 CFWriteStreamGetStatus((CFWriteStreamRef)stream) == kCFStreamStatusNotOpen) 457 { 458 if (val == kCFBooleanTrue) { 459 __CFBitSet(fileStream->flags, APPEND); 460 fileStream->offset = -1; // Can't offset and append on the stream 461 } else { 462 __CFBitClear(fileStream->flags, APPEND); 463 } 464 result = TRUE; 465 } 466 467 else if (CFEqual(prop, kCFStreamPropertyFileCurrentOffset)) { 468 469 if (!__CFBitIsSet(fileStream->flags, APPEND)) 470 { 471 result = CFNumberGetValue((CFNumberRef)val, kCFNumberSInt64Type, &(fileStream->offset)); 472 } 473 474 if ((fileStream->fd != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) { 475 result = FALSE; 476 } 477 } 478 479 return result; 480} 481 482static void *fileCreate(struct _CFStream *stream, void *info) { 483 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; 484 _CFFileStreamContext *newCtxt = (_CFFileStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFFileStreamContext), 0); 485 if (!newCtxt) return NULL; 486 newCtxt->url = ctxt->url; 487 if (newCtxt->url) { 488 CFRetain(newCtxt->url); 489 } 490 newCtxt->fd = ctxt->fd; 491#ifdef REAL_FILE_SCHEDULING 492 newCtxt->rlInfo.cffd = NULL; 493#else 494 newCtxt->scheduled = 0; 495#endif 496 newCtxt->flags = 0; 497 newCtxt->offset = -1; 498 return newCtxt; 499} 500 501static void fileFinalize(struct _CFStream *stream, void *info) { 502 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; 503 if (ctxt->fd > 0) { 504#ifdef REAL_FILE_SCHEDULING 505 if (ctxt->rlInfo.cffd) { 506 CFFileDescriptorInvalidate(ctxt->rlInfo.cffd); 507 CFRelease(ctxt->rlInfo.cffd); 508 ctxt->rlInfo.cffd = NULL; 509 } 510#endif 511 close(ctxt->fd); 512#ifdef REAL_FILE_SCHEDULING 513 } else if (ctxt->rlInfo.rlArray) { 514 CFRelease(ctxt->rlInfo.rlArray); 515#endif 516 } 517 if (ctxt->url) { 518 CFRelease(ctxt->url); 519 } 520 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); 521} 522 523static CFStringRef fileCopyDescription(struct _CFStream *stream, void *info) { 524 // This needs work 525 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; 526 if (ctxt->url) { 527 return CFCopyDescription(ctxt->url); 528 } else { 529 return CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("fd = %d"), ctxt->fd); 530 } 531} 532 533/* CFData stream callbacks */ 534typedef struct { 535 CFDataRef data; // Mutable if the stream was constructed writable 536 const UInt8 *loc; // Current location in the file 537 Boolean scheduled; 538 char _padding[3]; 539} _CFReadDataStreamContext; 540 541#define BUF_SIZE 1024 542typedef struct _CFStreamByteBuffer { 543 UInt8 *bytes; 544 CFIndex capacity, length; 545 struct _CFStreamByteBuffer *next; 546} _CFStreamByteBuffer; 547 548typedef struct { 549 _CFStreamByteBuffer *firstBuf, *currentBuf; 550 CFAllocatorRef bufferAllocator; 551 Boolean scheduled; 552 char _padding[3]; 553} _CFWriteDataStreamContext; 554 555static Boolean readDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { 556 _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info; 557 if (dataStream->scheduled) { 558 if (CFDataGetLength(dataStream->data) != 0) { 559 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); 560 } else { 561 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL); 562 } 563 } 564 errorCode->error = 0; 565 *openComplete = TRUE; 566 return TRUE; 567} 568 569static void readDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) { 570 _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info; 571 if (dataStream->scheduled == FALSE) { 572 dataStream->scheduled = TRUE; 573 if (CFReadStreamGetStatus((CFReadStreamRef)stream) != kCFStreamStatusOpen) 574 return; 575 if (CFDataGetBytePtr(dataStream->data) + CFDataGetLength(dataStream->data) > dataStream->loc) { 576 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); 577 } else { 578 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL); 579 } 580 } 581} 582 583static CFIndex dataRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info) { 584 _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; 585 const UInt8 *bytePtr = CFDataGetBytePtr(dataCtxt->data); 586 CFIndex length = CFDataGetLength(dataCtxt->data); 587 CFIndex bytesToCopy = bytePtr + length - dataCtxt->loc; 588 if (bytesToCopy > bufferLength) { 589 bytesToCopy = bufferLength; 590 } 591 if (bytesToCopy < 0) { 592 bytesToCopy = 0; 593 } 594 if (bytesToCopy != 0) { 595 memmove(buffer, dataCtxt->loc, bytesToCopy); 596 dataCtxt->loc += bytesToCopy; 597 } 598 error->error = 0; 599 *atEOF = (dataCtxt->loc < bytePtr + length) ? FALSE : TRUE; 600 if (dataCtxt->scheduled && !*atEOF) { 601 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); 602 } 603 return bytesToCopy; 604} 605 606static const UInt8 *dataGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info) { 607 _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; 608 const UInt8 *bytes = CFDataGetBytePtr(dataCtxt->data); 609 if (dataCtxt->loc - bytes > maxBytesToRead) { 610 *numBytesRead = maxBytesToRead; 611 *atEOF = FALSE; 612 } else { 613 *numBytesRead = dataCtxt->loc - bytes; 614 *atEOF = TRUE; 615 } 616 error->error = 0; 617 bytes = dataCtxt->loc; 618 dataCtxt->loc += *numBytesRead; 619 if (dataCtxt->scheduled && !*atEOF) { 620 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); 621 } 622 return bytes; 623} 624 625static Boolean dataCanRead(CFReadStreamRef stream, void *info) { 626 _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; 627 return (CFDataGetBytePtr(dataCtxt->data) + CFDataGetLength(dataCtxt->data) > dataCtxt->loc) ? TRUE : FALSE; 628} 629 630static Boolean writeDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { 631 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; 632 if (dataStream->scheduled) { 633 if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) { 634 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); 635 } else { 636 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL); 637 } 638 } 639 errorCode->error = 0; 640 *openComplete = TRUE; 641 return TRUE; 642} 643 644static void writeDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) { 645 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; 646 if (dataStream->scheduled == FALSE) { 647 dataStream->scheduled = TRUE; 648 if (CFWriteStreamGetStatus((CFWriteStreamRef)stream) != kCFStreamStatusOpen) 649 return; 650 if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) { 651 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); 652 } else { 653 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL); 654 } 655 } 656} 657 658static CFIndex dataWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) { 659 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; 660 CFIndex result; 661 CFIndex freeSpace = dataStream->currentBuf->capacity - dataStream->currentBuf->length; 662 if (dataStream->bufferAllocator == kCFAllocatorNull && bufferLength > freeSpace) { 663 errorCode->error = ENOMEM; 664 errorCode->domain = kCFStreamErrorDomainPOSIX; 665 return -1; 666 } else { 667 result = bufferLength; 668 while (bufferLength > 0) { 669 CFIndex amountToCopy = (bufferLength > freeSpace) ? freeSpace : bufferLength; 670 if (freeSpace > 0) { 671 memmove(dataStream->currentBuf->bytes + dataStream->currentBuf->length, buffer, amountToCopy); 672 buffer += amountToCopy; 673 bufferLength -= amountToCopy; 674 dataStream->currentBuf->length += amountToCopy; 675 } 676 if (bufferLength > 0) { 677 CFIndex bufSize = BUF_SIZE > bufferLength ? BUF_SIZE : bufferLength; 678 _CFStreamByteBuffer *newBuf = (_CFStreamByteBuffer *)CFAllocatorAllocate(dataStream->bufferAllocator, sizeof(_CFStreamByteBuffer) + bufSize, 0); 679 if (newBuf == NULL) { 680 errorCode->error = ENOMEM; 681 errorCode->domain = kCFStreamErrorDomainPOSIX; 682 return -1; 683 } else { 684 newBuf->bytes = (UInt8 *)(newBuf + 1); 685 newBuf->capacity = bufSize; 686 newBuf->length = 0; 687 newBuf->next = NULL; 688 dataStream->currentBuf->next = newBuf; 689 dataStream->currentBuf = newBuf; 690 freeSpace = bufSize; 691 } 692 } 693 } 694 errorCode->error = 0; 695 } 696 if (dataStream->scheduled && (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length)) { 697 CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL); 698 } 699 return result; 700} 701 702static Boolean dataCanWrite(CFWriteStreamRef stream, void *info) { 703 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; 704 if (dataStream->bufferAllocator != kCFAllocatorNull) return TRUE; 705 if (dataStream->currentBuf->capacity > dataStream->currentBuf->length) return TRUE; 706 return FALSE; 707} 708 709static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) { 710 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; 711 CFIndex size = 0; 712 _CFStreamByteBuffer *buf; 713 CFAllocatorRef alloc; 714 UInt8 *bytes, *currByte; 715 if (!CFEqual(propertyName, kCFStreamPropertyDataWritten)) return NULL; 716 if (dataStream->bufferAllocator == kCFAllocatorNull) return NULL; 717 alloc = dataStream->bufferAllocator; 718 for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { 719 size += buf->length; 720 } 721 bytes = (UInt8 *)CFAllocatorAllocate(alloc, size, 0); 722 currByte = bytes; 723 for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { 724 memmove(currByte, buf->bytes, buf->length); 725 currByte += buf->length; 726 } 727 return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc); 728} 729 730static void *readDataCreate(struct _CFStream *stream, void *info) { 731 _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info; 732 _CFReadDataStreamContext *newCtxt = (_CFReadDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFReadDataStreamContext), 0); 733 if (!newCtxt) return NULL; 734 newCtxt->data = (CFDataRef)CFRetain(ctxt->data); 735 newCtxt->loc = CFDataGetBytePtr(newCtxt->data); 736 newCtxt->scheduled = FALSE; 737 return (void *)newCtxt; 738} 739 740static void readDataFinalize(struct _CFStream *stream, void *info) { 741 _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info; 742 CFRelease(ctxt->data); 743 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); 744} 745 746static CFStringRef readDataCopyDescription(struct _CFStream *stream, void *info) { 747 return CFCopyDescription(((_CFReadDataStreamContext *)info)->data); 748} 749 750static void *writeDataCreate(struct _CFStream *stream, void *info) { 751 _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info; 752 _CFWriteDataStreamContext *newCtxt; 753 if (ctxt->bufferAllocator != kCFAllocatorNull) { 754 if (ctxt->bufferAllocator == NULL) ctxt->bufferAllocator = CFAllocatorGetDefault(); 755 CFRetain(ctxt->bufferAllocator); 756 newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer) + BUF_SIZE, 0); 757 newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt + 1); 758 newCtxt->firstBuf->bytes = (UInt8 *)(newCtxt->firstBuf + 1); 759 newCtxt->firstBuf->capacity = BUF_SIZE; 760 newCtxt->firstBuf->length = 0; 761 newCtxt->firstBuf->next = NULL; 762 newCtxt->currentBuf = newCtxt->firstBuf; 763 newCtxt->bufferAllocator = ctxt->bufferAllocator; 764 newCtxt->scheduled = FALSE; 765 } else { 766 newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer), 0); 767 newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt+1); 768 newCtxt->firstBuf->bytes = ctxt->firstBuf->bytes; 769 newCtxt->firstBuf->capacity = ctxt->firstBuf->capacity; 770 newCtxt->firstBuf->length = 0; 771 newCtxt->firstBuf->next = NULL; 772 newCtxt->currentBuf = newCtxt->firstBuf; 773 newCtxt->bufferAllocator = kCFAllocatorNull; 774 newCtxt->scheduled = FALSE; 775 } 776 return (void *)newCtxt; 777} 778 779static void writeDataFinalize(struct _CFStream *stream, void *info) { 780 _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info; 781 if (ctxt->bufferAllocator != kCFAllocatorNull) { 782 _CFStreamByteBuffer *buf = ctxt->firstBuf->next, *next; 783 while (buf != NULL) { 784 next = buf->next; 785 CFAllocatorDeallocate(ctxt->bufferAllocator, buf); 786 buf = next; 787 } 788 CFRelease(ctxt->bufferAllocator); 789 } 790 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); 791} 792 793static CFStringRef writeDataCopyDescription(struct _CFStream *stream, void *info) { 794 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFWriteDataContext %p>"), info); 795} 796 797static const struct _CFStreamCallBacksV1 fileCallBacks = {1, fileCreate, fileFinalize, fileCopyDescription, fileOpen, NULL, fileRead, NULL, fileCanRead, fileWrite, fileCanWrite, fileClose, fileCopyProperty, fileSetProperty, NULL, fileSchedule, fileUnschedule}; 798 799static struct _CFStream *_CFStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL, Boolean forReading) { 800 _CFFileStreamContext fileContext; 801 CFStringRef scheme = fileURL ? CFURLCopyScheme(fileURL) : NULL; 802 if (!scheme || !CFEqual(scheme, CFSTR("file"))) { 803 if (scheme) CFRelease(scheme); 804 return NULL; 805 } 806 CFRelease(scheme); 807 fileContext.url = fileURL; 808 fileContext.fd = -1; 809 return _CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), forReading); 810} 811 812CF_EXPORT CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) { 813 return (CFReadStreamRef)_CFStreamCreateWithFile(alloc, fileURL, TRUE); 814} 815 816CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) { 817 return (CFWriteStreamRef)_CFStreamCreateWithFile(alloc, fileURL, FALSE); 818} 819 820// CFReadStreamRef takes ownership of the fd, and will close() it 821CFReadStreamRef _CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) { 822 _CFFileStreamContext fileContext; 823 fileContext.url = NULL; 824 fileContext.fd = fd; 825 return (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), TRUE); 826} 827 828// CFWriteStreamRef takes ownership of the fd, and will close() it 829CFWriteStreamRef _CFWriteStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) { 830 _CFFileStreamContext fileContext; 831 fileContext.url = NULL; 832 fileContext.fd = fd; 833 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), FALSE); 834} 835 836 837 838static const struct _CFStreamCallBacksV1 readDataCallBacks = {1, readDataCreate, readDataFinalize, readDataCopyDescription, readDataOpen, NULL, dataRead, dataGetBuffer, dataCanRead, NULL, NULL, NULL, NULL, NULL, NULL, readDataSchedule, NULL}; 839static const struct _CFStreamCallBacksV1 writeDataCallBacks = {1, writeDataCreate, writeDataFinalize, writeDataCopyDescription, writeDataOpen, NULL, NULL, NULL, NULL, dataWrite, dataCanWrite, NULL, dataCopyProperty, NULL, NULL, writeDataSchedule, NULL}; 840 841CF_EXPORT CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) { 842 _CFReadDataStreamContext ctxt; 843 CFReadStreamRef result; 844 ctxt.data = CFDataCreateWithBytesNoCopy(alloc, bytes, length, bytesDeallocator); 845 result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE); 846 CFRelease(ctxt.data); 847 return result; 848} 849 850/* This needs to be exported to make it callable from Foundation. */ 851CF_EXPORT CFReadStreamRef CFReadStreamCreateWithData(CFAllocatorRef alloc, CFDataRef data) { 852 _CFReadDataStreamContext ctxt; 853 CFReadStreamRef result = NULL; 854 855 ctxt.data = (CFDataRef)CFRetain(data); 856 result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE); 857 CFRelease(data); 858 return result; 859} 860 861CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity) { 862 _CFStreamByteBuffer buf; 863 _CFWriteDataStreamContext ctxt; 864 buf.bytes = buffer; 865 buf.capacity = bufferCapacity; 866 buf.length = 0; 867 buf.next = NULL; 868 ctxt.firstBuf = &buf; 869 ctxt.currentBuf = ctxt.firstBuf; 870 ctxt.bufferAllocator = kCFAllocatorNull; 871 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE); 872} 873 874CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator) { 875 _CFWriteDataStreamContext ctxt; 876 ctxt.firstBuf = NULL; 877 ctxt.currentBuf = NULL; 878 ctxt.bufferAllocator = bufferAllocator; 879 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE); 880} 881 882#undef BUF_SIZE 883 884