2  * Copyright (c) 2000-2001,2003-2004,2011-2012,2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  26 // unix++ - C++ layer for basic UNIX facilities 
  29 #include <security_utilities/cfutilities.h> 
  30 #include <security_utilities/memutils.h> 
  31 #include <security_utilities/debugging.h> 
  32 #include <sys/dirent.h> 
  33 #include <sys/xattr.h> 
  35 #include <IOKit/IOKitLib.h> 
  36 #include <IOKit/IOKitKeys.h> 
  37 #include <IOKit/storage/IOStorageDeviceCharacteristics.h> 
  41 namespace UnixPlusPlus 
{ 
  43 using LowLevelMemoryUtilities::increment
; 
  47 // Canonical open of a file descriptor. All other open operations channel through this. 
  48 // Note that we abuse the S_IFMT mode flags as operational options. 
  50 void FileDesc::open(const char *path
, int flags
, mode_t mode
) 
  52         if ((mFd 
= ::open(path
, flags
, mode 
& ~S_IFMT
)) == -1) { 
  53                 if (errno 
== ENOENT 
&& (mode 
& S_IFMT
) == modeMissingOk
) { 
  60     secdebug("unixio", "open(%s,0x%x,0x%x) = %d", path
, flags
, mode
, mFd
); 
  63 void FileDesc::close() 
  66         checkError(::close(mFd
)); 
  67         secdebug("unixio", "close(%d)", mFd
); 
  74 // Filedescoid operations 
  76 size_t FileDesc::read(void *addr
, size_t length
) 
  78     switch (ssize_t rc 
= ::read(mFd
, addr
, length
)) { 
  79     case 0:             // end-of-source 
  80         if (length 
== 0) { // check for errors, but don't set mAtEnd unless we have to 
  81             secdebug("unixio", "%d zero read (ignored)", mFd
); 
  85         secdebug("unixio", "%d end of data", mFd
); 
  89             return 0;   // no data, unknown end-of-source status 
  90         UnixError::throwMe(); // throw error 
  96 size_t FileDesc::write(const void *addr
, size_t length
) 
  98     ssize_t rc 
= ::write(mFd
, addr
, length
); 
 102         UnixError::throwMe(); 
 109 // I/O with integral positioning. 
 110 // These don't affect file position and the atEnd() flag; and they 
 111 // don't make allowances for asynchronous I/O. 
 113 size_t FileDesc::read(void *addr
, size_t length
, size_t position
) 
 115         return checkError(::pread(mFd
, addr
, length
, position
)); 
 118 size_t FileDesc::write(const void *addr
, size_t length
, size_t position
) 
 120         return checkError(::pwrite(mFd
, addr
, length
, position
)); 
 125 // Waiting (repeating) I/O 
 127 size_t FileDesc::readAll(void *addr
, size_t length
) 
 130         while (length 
> 0 && !atEnd()) { 
 131                 size_t size 
= read(addr
, length
); 
 132                 addr 
= increment(addr
, size
); 
 139 size_t FileDesc::readAll(string 
&value
) 
 144                 if (size_t size 
= read(buffer
, sizeof(buffer
))) { 
 145                         s 
+= string(buffer
, size
); 
 150         return value
.length(); 
 154 void FileDesc::writeAll(const void *addr
, size_t length
) 
 157                 size_t size 
= write(addr
, length
); 
 158                 addr 
= increment(addr
, size
); 
 167 size_t FileDesc::seek(size_t position
, int whence
) 
 169     return (size_t)checkError(::lseek(mFd
, position
, whence
)); 
 172 size_t FileDesc::position() const 
 174         return (size_t)checkError(::lseek(mFd
, 0, SEEK_CUR
)); 
 181 void *FileDesc::mmap(int prot
, size_t length
, int flags
, size_t offset
, void *addr
) 
 183         if (!(flags 
& (MAP_PRIVATE 
| MAP_SHARED
)))      // one is required 
 184                 flags 
|= MAP_PRIVATE
; 
 185     void *result 
= ::mmap(addr
, length 
? length 
: fileSize(), prot
, flags
, mFd
, offset
); 
 186     if (result 
== MAP_FAILED
) 
 187         UnixError::throwMe(); 
 193 // Basic fcntl support 
 195 int FileDesc::fcntl(int cmd
, void *arg
) const 
 197     int rc 
= ::fcntl(mFd
, cmd
, arg
); 
 198     secdebug("unixio", "%d fcntl(%d,%p) = %d", mFd
, cmd
, arg
, rc
); 
 199         return checkError(rc
); 
 206 void FileDesc::setFlag(int flag
, bool on
) const 
 208     if (flag
) {         // if there's anything at all to do... 
 209         int oldFlags 
= flags(); 
 210         flags(on 
? (oldFlags 
| flag
) : (oldFlags 
& ~flag
)); 
 216 // Duplication operations 
 218 FileDesc 
FileDesc::dup() const 
 220         return FileDesc(checkError(::dup(mFd
)), atEnd()); 
 223 FileDesc 
FileDesc::dup(int newFd
) const 
 225         return FileDesc(checkError(::dup2(mFd
, newFd
)), atEnd()); 
 230 // Advisory locking, fcntl style 
 232 void FileDesc::lock(int type
, const Pos 
&pos
) 
 234         LockArgs 
args(type
, pos
); 
 235         IFDEBUG(args
.debug(fd(), "lock")); 
 236         checkError(fcntl(F_SETLKW
, &args
)); 
 239 bool FileDesc::tryLock(int type
, const Pos 
&pos
) 
 241         LockArgs 
args(type
, pos
); 
 242         IFDEBUG(args
.debug(fd(), "tryLock")); 
 244                 fcntl(F_SETLK
, &args
); 
 246         } catch (const UnixError 
&err
) { 
 247                 if (err
.error 
== EAGAIN
) 
 256 void FileDesc::LockArgs::debug(int fd
, const char *what
) 
 258         secdebug("fdlock", "%d %s %s:%ld(%ld)", fd
, what
, 
 259                 (l_whence 
== SEEK_SET
) ? "ABS" : (l_whence 
== SEEK_CUR
) ? "REL" : "END", 
 260                 long(l_start
), long(l_len
)); 
 269 int FileDesc::ioctl(int cmd
, void *arg
) const 
 271     int rc 
= ::ioctl(mFd
, cmd
, arg
); 
 273         UnixError::throwMe(); 
 281 void FileDesc::setAttr(const char *name
, const void *value
, size_t length
, 
 282         u_int32_t position 
/* = 0 */, int options 
/* = 0 */) 
 284         checkError(::fsetxattr(mFd
, name
, value
, length
, position
, options
)); 
 287 ssize_t 
FileDesc::getAttrLength(const char *name
) 
 289         ssize_t rc 
= ::fgetxattr(mFd
, name
, NULL
, 0, 0, 0); 
 295                         UnixError::throwMe(); 
 300 ssize_t 
FileDesc::getAttr(const char *name
, void *value
, size_t length
, 
 301         u_int32_t position 
/* = 0 */, int options 
/* = 0 */) 
 303     ssize_t rc 
= ::fgetxattr(mFd
, name
, value
, length
, position
, options
); 
 309                         UnixError::throwMe(); 
 314 void FileDesc::removeAttr(const char *name
, int options 
/* = 0 */) 
 316         if (::fremovexattr(mFd
, name
, options
)) 
 319                         if (!(options 
& XATTR_REPLACE
)) // somewhat mis-using an API flag here... 
 320                                 return;         // attribute not found; we'll call that okay 
 323                         UnixError::throwMe(); 
 327 size_t FileDesc::listAttr(char *value
, size_t length
, int options 
/* = 0 */) 
 329         return checkError(::flistxattr(mFd
, value
, length
, options
)); 
 333 void FileDesc::setAttr(const std::string 
&name
, const std::string 
&value
, int options 
/* = 0 */) 
 335         return setAttr(name
, value
.c_str(), value
.size(), 0, options
); 
 338 std::string 
FileDesc::getAttr(const std::string 
&name
, int options 
/* = 0 */) 
 340         char buffer
[4096];      //@@@ auto-expand? 
 341         ssize_t length 
= getAttr(name
, buffer
, sizeof(buffer
), 0, options
); 
 343                 return string(buffer
, length
); 
 348 bool FileDesc::isPlainFile(const std::string 
&path
) 
 352         if (::lstat(path
.c_str(), &st2
)) 
 353                 UnixError::throwMe(); 
 355         return (st1
.st_ino 
== st2
.st_ino 
&& S_ISREG(st2
.st_mode
)); 
 361 void FileDesc::fstat(UnixStat 
&st
) const 
 363     if (::fstat(mFd
, &st
)) 
 364         UnixError::throwMe(); 
 367 size_t FileDesc::fileSize() const 
 371     return (size_t)st
.st_size
; 
 374 bool FileDesc::isA(int mode
) const 
 378         return (st
.st_mode 
& S_IFMT
) == mode
; 
 382 void FileDesc::chown(uid_t uid
) 
 384         checkError(::fchown(mFd
, uid
, gid_t(-1))); 
 387 void FileDesc::chown(uid_t uid
, gid_t gid
) 
 389         checkError(::fchown(mFd
, uid
, gid
)); 
 392 void FileDesc::chgrp(gid_t gid
) 
 394         checkError(::fchown(mFd
, uid_t(-1), gid
)); 
 397 void FileDesc::chmod(mode_t mode
) 
 399         checkError(::fchmod(mFd
, mode
)); 
 402 void FileDesc::chflags(u_int flags
) 
 404         checkError(::fchflags(mFd
, flags
)); 
 408 FILE *FileDesc::fdopen(const char *form
) 
 410         //@@@ pick default value for 'form' based on chracteristics of mFd 
 411     return ::fdopen(mFd
, form
); 
 416 // Device characteristics 
 418 static CFDictionaryRef 
deviceCharacteristics(FileDesc 
&fd
) 
 421         AutoFileDesc::UnixStat st
; 
 423         char buffer
[MAXNAMLEN
]; 
 424         checkError(::devname_r(st
.st_dev
, S_IFBLK
, buffer
, MAXNAMLEN
)); 
 426         // search in IO Registry for named device 
 427         CFDictionaryRef matching 
= IOBSDNameMatching(kIOMasterPortDefault
, 0, buffer
); 
 429                 // fetch the object with the matching BSD name (consumes reference on matching) 
 430                 io_registry_entry_t entry 
= IOServiceGetMatchingService(kIOMasterPortDefault
, matching
); 
 431                 if (entry 
!= IO_OBJECT_NULL
) { 
 432                         // get device characteristics 
 433                         CFDictionaryRef characteristics 
= (CFDictionaryRef
)IORegistryEntrySearchCFProperty(entry
, 
 435                                 CFSTR(kIOPropertyDeviceCharacteristicsKey
), 
 437                                 kIORegistryIterateRecursively 
| kIORegistryIterateParents
); 
 438                         IOObjectRelease(entry
); 
 439                         return characteristics
; 
 443         return NULL
;    // unable to get device characteristics 
 446 std::string 
FileDesc::mediumType() 
 448         CFRef
<CFDictionaryRef
> characteristics 
= deviceCharacteristics(*this); 
 449         if (characteristics
) { 
 450                 CFStringRef mediumType 
= (CFStringRef
)CFDictionaryGetValue(characteristics
, CFSTR(kIOPropertyMediumTypeKey
)); 
 452                         return cfString(mediumType
); 
 459 // Signals and signal masks 
 461 SigSet 
sigMask(SigSet set
, int how 
/* = SIG_SETMASK */) 
 464         checkError(::sigprocmask(how
, &set
.value(), &old
)); 
 470 // Make or use a directory, open-style. 
 472 // Flags are to be interpreted like open(2) flags; particularly 
 473 //      O_CREAT         make the directory if not present 
 474 //      O_EXCL          fail if the directory is present 
 475 // Other open(2) flags are currently ignored. 
 477 // Yes, it's a function. 
 479 void makedir(const char *path
, int flags
, mode_t mode
) 
 482         if (!stat(path
, &st
)) { 
 484                         UnixError::throwMe(EEXIST
); 
 485                 if (!S_ISDIR(st
.st_mode
)) 
 486                         UnixError::throwMe(ENOTDIR
); 
 487                 secdebug("makedir", "%s exists", path
); 
 492         if (errno 
!= ENOENT 
|| !(flags 
& O_CREAT
)) 
 493                 UnixError::throwMe(); 
 495         // ENOENT and creation enabled 
 496         if (::mkdir(path
, mode
)) { 
 497                 if (errno 
== EEXIST 
&& !(flags 
& O_EXCL
)) 
 498                         return;         // fine (race condition, resolved) 
 499                 UnixError::throwMe(); 
 501         secdebug("makedir", "%s created", path
); 
 506 // Open, read/write, close a (small) file on disk 
 508 int ffprintf(const char *path
, int flags
, mode_t mode
, const char *format
, ...) 
 510         FileDesc 
fd(path
, flags
, mode
); 
 511         FILE *f 
= fd
.fdopen("w"); 
 513         va_start(args
, format
); 
 514         int rc 
= vfprintf(f
, format
, args
); 
 517                 UnixError::throwMe(); 
 521 int ffscanf(const char *path
, const char *format
, ...) 
 523         if (FILE *f 
= fopen(path
, "r")) { 
 525                 va_start(args
, format
); 
 526                 int rc 
= vfscanf(f
, format
, args
); 
 531         UnixError::throwMe(); 
 535 }       // end namespace IPPlusPlus 
 536 }       // end namespace Security