2  * Copyright (c) 2000-2001,2003-2004,2011-2012,2014-2016 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/cfmunge.h> 
  31 #include <security_utilities/memutils.h> 
  32 #include <security_utilities/debugging.h> 
  33 #include <sys/dirent.h> 
  34 #include <sys/xattr.h> 
  36 #include <IOKit/IOKitLib.h> 
  37 #include <IOKit/IOKitKeys.h> 
  38 #include <IOKit/IOBSD.h> 
  39 #include <IOKit/storage/IOStorageDeviceCharacteristics.h> 
  43 namespace UnixPlusPlus 
{ 
  45 using LowLevelMemoryUtilities::increment
; 
  49 // Canonical open of a file descriptor. All other open operations channel through this. 
  50 // Note that we abuse the S_IFMT mode flags as operational options. 
  52 void FileDesc::open(const char *path
, int flags
, mode_t mode
) 
  54         if ((mFd 
= ::open(path
, flags
, mode 
& ~S_IFMT
)) == -1) { 
  55                 if (errno 
== ENOENT 
&& (mode 
& S_IFMT
) == modeMissingOk
) { 
  62     secinfo("unixio", "open(%s,0x%x,0x%x) = %d", path
, flags
, mode
, mFd
); 
  65 void FileDesc::close() 
  68         checkError(::close(mFd
)); 
  69         secinfo("unixio", "close(%d)", mFd
); 
  76 // Filedescoid operations 
  78 size_t FileDesc::read(void *addr
, size_t length
) 
  80     switch (ssize_t rc 
= ::read(mFd
, addr
, length
)) { 
  81     case 0:             // end-of-source 
  82         if (length 
== 0) { // check for errors, but don't set mAtEnd unless we have to 
  83             secinfo("unixio", "%d zero read (ignored)", mFd
); 
  87         secinfo("unixio", "%d end of data", mFd
); 
  91             return 0;   // no data, unknown end-of-source status 
  92         UnixError::throwMe(); // throw error 
  98 size_t FileDesc::write(const void *addr
, size_t length
) 
 100     ssize_t rc 
= ::write(mFd
, addr
, length
); 
 104         UnixError::throwMe(); 
 111 // I/O with integral positioning. 
 112 // These don't affect file position and the atEnd() flag; and they 
 113 // don't make allowances for asynchronous I/O. 
 115 size_t FileDesc::read(void *addr
, size_t length
, size_t position
) 
 117         return checkError(::pread(mFd
, addr
, length
, position
)); 
 120 size_t FileDesc::write(const void *addr
, size_t length
, size_t position
) 
 122         return checkError(::pwrite(mFd
, addr
, length
, position
)); 
 127 // Waiting (repeating) I/O 
 129 size_t FileDesc::readAll(void *addr
, size_t length
) 
 132         while (length 
> 0 && !atEnd()) { 
 133                 size_t size 
= read(addr
, length
); 
 134                 addr 
= increment(addr
, size
); 
 141 size_t FileDesc::readAll(string 
&value
) 
 146                 if (size_t size 
= read(buffer
, sizeof(buffer
))) { 
 147                         s 
+= string(buffer
, size
); 
 152         return value
.length(); 
 156 void FileDesc::writeAll(const void *addr
, size_t length
) 
 159                 size_t size 
= write(addr
, length
); 
 160                 addr 
= increment(addr
, size
); 
 166 void FileDesc::truncate(size_t offset
) 
 168     UnixError::check(ftruncate(mFd
, offset
)); 
 175 size_t FileDesc::seek(size_t position
, int whence
) 
 177     return (size_t)checkError(::lseek(mFd
, position
, whence
)); 
 180 size_t FileDesc::position() const 
 182         return (size_t)checkError(::lseek(mFd
, 0, SEEK_CUR
)); 
 189 void *FileDesc::mmap(int prot
, size_t length
, int flags
, size_t offset
, void *addr
) 
 191         if (!(flags 
& (MAP_PRIVATE 
| MAP_SHARED
)))      // one is required 
 192                 flags 
|= MAP_PRIVATE
; 
 193     void *result 
= ::mmap(addr
, length 
? length 
: fileSize(), prot
, flags
, mFd
, offset
); 
 194     if (result 
== MAP_FAILED
) 
 195         UnixError::throwMe(); 
 201 // Basic fcntl support 
 203 int FileDesc::fcntl(int cmd
, void *arg
) const 
 205     int rc 
= ::fcntl(mFd
, cmd
, arg
); 
 206     secinfo("unixio", "%d fcntl(%d,%p) = %d", mFd
, cmd
, arg
, rc
); 
 207         return checkError(rc
); 
 214 void FileDesc::setFlag(int flag
, bool on
) const 
 216     if (flag
) {         // if there's anything at all to do... 
 217         int oldFlags 
= flags(); 
 218         flags(on 
? (oldFlags 
| flag
) : (oldFlags 
& ~flag
)); 
 224 // Duplication operations 
 226 FileDesc 
FileDesc::dup() const 
 228         return FileDesc(checkError(::dup(mFd
)), atEnd()); 
 231 FileDesc 
FileDesc::dup(int newFd
) const 
 233         return FileDesc(checkError(::dup2(mFd
, newFd
)), atEnd()); 
 238 // Advisory locking, fcntl style 
 240 void FileDesc::lock(int type
, const Pos 
&pos
) 
 242         LockArgs 
args(type
, pos
); 
 243         IFDEBUG(args
.debug(fd(), "lock")); 
 244         checkError(fcntl(F_SETLKW
, &args
)); 
 247 bool FileDesc::tryLock(int type
, const Pos 
&pos
) 
 249         LockArgs 
args(type
, pos
); 
 250         IFDEBUG(args
.debug(fd(), "tryLock")); 
 252                 fcntl(F_SETLK
, &args
); 
 254         } catch (const UnixError 
&err
) { 
 255                 if (err
.error 
== EAGAIN
) 
 264 void FileDesc::LockArgs::debug(int fd
, const char *what
) 
 266         secinfo("fdlock", "%d %s %s:%ld(%ld)", fd
, what
, 
 267                 (l_whence 
== SEEK_SET
) ? "ABS" : (l_whence 
== SEEK_CUR
) ? "REL" : "END", 
 268                 long(l_start
), long(l_len
)); 
 277 int FileDesc::ioctl(int cmd
, void *arg
) const 
 279     int rc 
= ::ioctl(mFd
, cmd
, arg
); 
 281         UnixError::throwMe(); 
 289 void FileDesc::setAttr(const char *name
, const void *value
, size_t length
, 
 290         u_int32_t position 
/* = 0 */, int options 
/* = 0 */) 
 292         checkError(::fsetxattr(mFd
, name
, value
, length
, position
, options
)); 
 295 ssize_t 
FileDesc::getAttrLength(const char *name
, int options
) 
 297         ssize_t rc 
= ::fgetxattr(mFd
, name
, NULL
, 0, 0, options
); 
 303                         UnixError::throwMe(); 
 308 ssize_t 
FileDesc::getAttr(const char *name
, void *value
, size_t length
, 
 309         u_int32_t position 
/* = 0 */, int options 
/* = 0 */) 
 311     ssize_t rc 
= ::fgetxattr(mFd
, name
, value
, length
, position
, options
); 
 317                         UnixError::throwMe(); 
 322 void FileDesc::removeAttr(const char *name
, int options 
/* = 0 */) 
 324         if (::fremovexattr(mFd
, name
, options
)) 
 327                         if (!(options 
& XATTR_REPLACE
)) // somewhat mis-using an API flag here... 
 328                                 return;         // attribute not found; we'll call that okay 
 331                         UnixError::throwMe(); 
 335 size_t FileDesc::listAttr(char *value
, size_t length
, int options 
/* = 0 */) 
 337         return checkError(::flistxattr(mFd
, value
, length
, options
)); 
 341 void FileDesc::setAttr(const std::string 
&name
, const std::string 
&value
, int options 
/* = 0 */) 
 343         return setAttr(name
, value
.c_str(), value
.size(), 0, options
); 
 346 std::string 
FileDesc::getAttr(const std::string 
&name
, int options 
/* = 0 */) 
 348         char buffer
[4096];      //@@@ auto-expand? 
 349         ssize_t length 
= getAttr(name
, buffer
, sizeof(buffer
), 0, options
); 
 351                 return string(buffer
, length
); 
 357 static bool checkFork(ssize_t rc
) 
 360         case 0:         // empty fork, produced by NFS/AFP et al; ignore 
 362         default:        // non-empty fork present, fail 
 364         case -1:        // failed system call; let's see... 
 367                         return false;           // not present, no problem 
 369                         return false;           // HFS+ returns that if we ask for Resource Forks on anything but plain files (e.g. directories) 
 371                         UnixError::throwMe(); 
 376 bool filehasExtendedAttribute(const char *path
, const char *forkname
) 
 378         return checkFork(::getxattr(path
, forkname
, NULL
, 0, 0, 0)); 
 381 bool FileDesc::hasExtendedAttribute(const char *forkname
) const 
 383         return checkFork(::fgetxattr(mFd
, forkname
, NULL
, 0, 0, 0)); 
 386 bool FileDesc::isPlainFile(const std::string 
&path
) 
 390         if (::lstat(path
.c_str(), &st2
)) 
 391                 UnixError::throwMe(); 
 393         return (st1
.st_ino 
== st2
.st_ino 
&& S_ISREG(st2
.st_mode
)); 
 399 void FileDesc::fstat(UnixStat 
&st
) const 
 401     if (::fstat(mFd
, &st
)) 
 402         UnixError::throwMe(); 
 405 size_t FileDesc::fileSize() const 
 409     return (size_t)st
.st_size
; 
 412 bool FileDesc::isA(int mode
) const 
 416         return (st
.st_mode 
& S_IFMT
) == mode
; 
 420 void FileDesc::chown(uid_t uid
) 
 422         checkError(::fchown(mFd
, uid
, gid_t(-1))); 
 425 void FileDesc::chown(uid_t uid
, gid_t gid
) 
 427         checkError(::fchown(mFd
, uid
, gid
)); 
 430 void FileDesc::chgrp(gid_t gid
) 
 432         checkError(::fchown(mFd
, uid_t(-1), gid
)); 
 435 void FileDesc::chmod(mode_t mode
) 
 437         checkError(::fchmod(mFd
, mode
)); 
 440 void FileDesc::chflags(u_int flags
) 
 442         checkError(::fchflags(mFd
, flags
)); 
 446 FILE *FileDesc::fdopen(const char *form
) 
 448         //@@@ pick default value for 'form' based on chracteristics of mFd 
 449     return ::fdopen(mFd
, form
); 
 454 // Device characteristics 
 456 static CFDictionaryRef 
deviceCharacteristics(FileDesc 
&fd
) 
 459         FileDesc::UnixStat st
; 
 461         CFTemp
<CFDictionaryRef
> matching("{%s=%d,%s=%d}", 
 462                 kIOBSDMajorKey
, major(st
.st_dev
), 
 463                 kIOBSDMinorKey
, minor(st
.st_dev
) 
 465         // IOServiceGetMatchingService CONSUMES its dictionary argument(!) 
 466         io_registry_entry_t entry 
= IOServiceGetMatchingService(kIOMasterPortDefault
, matching
.yield()); 
 467         if (entry 
!= IO_OBJECT_NULL
) { 
 468                 // get device characteristics 
 469                 CFDictionaryRef characteristics 
= (CFDictionaryRef
)IORegistryEntrySearchCFProperty(entry
, 
 471                         CFSTR(kIOPropertyDeviceCharacteristicsKey
), 
 473                         kIORegistryIterateRecursively 
| kIORegistryIterateParents
); 
 474                 IOObjectRelease(entry
); 
 475                 return characteristics
; 
 478         return NULL
;    // unable to get device characteristics 
 481 std::string 
FileDesc::mediumType() 
 483         CFRef
<CFDictionaryRef
> characteristics 
= deviceCharacteristics(*this); 
 484         if (characteristics
) { 
 485                 CFStringRef mediumType 
= (CFStringRef
)CFDictionaryGetValue(characteristics
, CFSTR(kIOPropertyMediumTypeKey
)); 
 487                         return cfString(mediumType
); 
 494 // Signals and signal masks 
 496 SigSet 
sigMask(SigSet set
, int how 
/* = SIG_SETMASK */) 
 499         checkError(::sigprocmask(how
, &set
.value(), &old
)); 
 505 // Make or use a directory, open-style. 
 507 // Flags are to be interpreted like open(2) flags; particularly 
 508 //      O_CREAT         make the directory if not present 
 509 //      O_EXCL          fail if the directory is present 
 510 // Other open(2) flags are currently ignored. 
 512 // Yes, it's a function. 
 514 void makedir(const char *path
, int flags
, mode_t mode
) 
 517         if (!stat(path
, &st
)) { 
 519                         UnixError::throwMe(EEXIST
); 
 520                 if (!S_ISDIR(st
.st_mode
)) 
 521                         UnixError::throwMe(ENOTDIR
); 
 522                 secinfo("makedir", "%s exists", path
); 
 527         if (errno 
!= ENOENT 
|| !(flags 
& O_CREAT
)) 
 528                 UnixError::throwMe(); 
 530         // ENOENT and creation enabled 
 531         if (::mkdir(path
, mode
)) { 
 532                 if (errno 
== EEXIST 
&& !(flags 
& O_EXCL
)) 
 533                         return;         // fine (race condition, resolved) 
 534                 UnixError::throwMe(); 
 536         secinfo("makedir", "%s created", path
); 
 541 // Open, read/write, close a (small) file on disk 
 543 int ffprintf(const char *path
, int flags
, mode_t mode
, const char *format
, ...) 
 545         FileDesc 
fd(path
, flags
, mode
); 
 546         FILE *f 
= fd
.fdopen("w"); 
 548         va_start(args
, format
); 
 549         int rc 
= vfprintf(f
, format
, args
); 
 552                 UnixError::throwMe(); 
 556 int ffscanf(const char *path
, const char *format
, ...) 
 558         if (FILE *f 
= fopen(path
, "r")) { 
 560                 va_start(args
, format
); 
 561                 int rc 
= vfscanf(f
, format
, args
); 
 566         UnixError::throwMe(); 
 570 }       // end namespace IPPlusPlus 
 571 }       // end namespace Security