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
);
74 void FileDesc::closeAndLog()
80 while((result
= ::close(mFd
)) == -1 && retryCount
) {
88 secinfo("unixio", "close(%d) error %d", mFd
, error
);
93 secinfo("unixio", "close(%d) err: %d", mFd
, error
);
99 // Filedescoid operations
101 size_t FileDesc::read(void *addr
, size_t length
)
103 switch (ssize_t rc
= ::read(mFd
, addr
, length
)) {
104 case 0: // end-of-source
105 if (length
== 0) { // check for errors, but don't set mAtEnd unless we have to
106 secinfo("unixio", "%d zero read (ignored)", mFd
);
110 secinfo("unixio", "%d end of data", mFd
);
114 return 0; // no data, unknown end-of-source status
115 UnixError::throwMe(); // throw error
116 default: // have data
121 size_t FileDesc::write(const void *addr
, size_t length
)
123 ssize_t rc
= ::write(mFd
, addr
, length
);
127 UnixError::throwMe();
134 // I/O with integral positioning.
135 // These don't affect file position and the atEnd() flag; and they
136 // don't make allowances for asynchronous I/O.
138 size_t FileDesc::read(void *addr
, size_t length
, size_t position
)
140 return checkError(::pread(mFd
, addr
, length
, position
));
143 size_t FileDesc::write(const void *addr
, size_t length
, size_t position
)
145 return checkError(::pwrite(mFd
, addr
, length
, position
));
150 // Waiting (repeating) I/O
152 size_t FileDesc::readAll(void *addr
, size_t length
)
155 while (length
> 0 && !atEnd()) {
156 size_t size
= read(addr
, length
);
157 addr
= increment(addr
, size
);
164 size_t FileDesc::readAll(string
&value
)
169 if (size_t size
= read(buffer
, sizeof(buffer
))) {
170 s
+= string(buffer
, size
);
175 return value
.length();
179 void FileDesc::writeAll(const void *addr
, size_t length
)
182 size_t size
= write(addr
, length
);
183 addr
= increment(addr
, size
);
189 void FileDesc::truncate(size_t offset
)
191 UnixError::check(ftruncate(mFd
, offset
));
198 size_t FileDesc::seek(size_t position
, int whence
)
200 return (size_t)checkError(::lseek(mFd
, position
, whence
));
203 size_t FileDesc::position() const
205 return (size_t)checkError(::lseek(mFd
, 0, SEEK_CUR
));
212 void *FileDesc::mmap(int prot
, size_t length
, int flags
, size_t offset
, void *addr
)
214 if (!(flags
& (MAP_PRIVATE
| MAP_SHARED
))) // one is required
215 flags
|= MAP_PRIVATE
;
216 void *result
= ::mmap(addr
, length
? length
: fileSize(), prot
, flags
, mFd
, offset
);
217 if (result
== MAP_FAILED
)
218 UnixError::throwMe();
224 // Basic fcntl support
226 int FileDesc::fcntl(int cmd
, void *arg
) const
228 int rc
= ::fcntl(mFd
, cmd
, arg
);
229 secinfo("unixio", "%d fcntl(%d,%p) = %d", mFd
, cmd
, arg
, rc
);
230 return checkError(rc
);
237 void FileDesc::setFlag(int flag
, bool on
) const
239 if (flag
) { // if there's anything at all to do...
240 int oldFlags
= flags();
241 flags(on
? (oldFlags
| flag
) : (oldFlags
& ~flag
));
247 // Duplication operations
249 FileDesc
FileDesc::dup() const
251 return FileDesc(checkError(::dup(mFd
)), atEnd());
254 FileDesc
FileDesc::dup(int newFd
) const
256 return FileDesc(checkError(::dup2(mFd
, newFd
)), atEnd());
261 // Advisory locking, fcntl style
263 void FileDesc::lock(int type
, const Pos
&pos
)
265 LockArgs
args(type
, pos
);
266 IFDEBUG(args
.debug(fd(), "lock"));
267 checkError(fcntl(F_SETLKW
, &args
));
270 bool FileDesc::tryLock(int type
, const Pos
&pos
)
272 LockArgs
args(type
, pos
);
273 IFDEBUG(args
.debug(fd(), "tryLock"));
275 fcntl(F_SETLK
, &args
);
277 } catch (const UnixError
&err
) {
278 if (err
.error
== EAGAIN
)
287 void FileDesc::LockArgs::debug(int fd
, const char *what
)
289 secinfo("fdlock", "%d %s %s:%ld(%ld)", fd
, what
,
290 (l_whence
== SEEK_SET
) ? "ABS" : (l_whence
== SEEK_CUR
) ? "REL" : "END",
291 long(l_start
), long(l_len
));
300 int FileDesc::ioctl(int cmd
, void *arg
) const
302 int rc
= ::ioctl(mFd
, cmd
, arg
);
304 UnixError::throwMe();
312 void FileDesc::setAttr(const char *name
, const void *value
, size_t length
,
313 u_int32_t position
/* = 0 */, int options
/* = 0 */)
315 checkError(::fsetxattr(mFd
, name
, value
, length
, position
, options
));
318 ssize_t
FileDesc::getAttrLength(const char *name
, int options
)
320 ssize_t rc
= ::fgetxattr(mFd
, name
, NULL
, 0, 0, options
);
326 UnixError::throwMe();
331 ssize_t
FileDesc::getAttr(const char *name
, void *value
, size_t length
,
332 u_int32_t position
/* = 0 */, int options
/* = 0 */)
334 ssize_t rc
= ::fgetxattr(mFd
, name
, value
, length
, position
, options
);
340 UnixError::throwMe();
345 void FileDesc::removeAttr(const char *name
, int options
/* = 0 */)
347 if (::fremovexattr(mFd
, name
, options
))
350 if (!(options
& XATTR_REPLACE
)) // somewhat mis-using an API flag here...
351 return; // attribute not found; we'll call that okay
354 UnixError::throwMe();
358 size_t FileDesc::listAttr(char *value
, size_t length
, int options
/* = 0 */)
360 return checkError(::flistxattr(mFd
, value
, length
, options
));
364 void FileDesc::setAttr(const std::string
&name
, const std::string
&value
, int options
/* = 0 */)
366 return setAttr(name
, value
.c_str(), value
.size(), 0, options
);
369 std::string
FileDesc::getAttr(const std::string
&name
, int options
/* = 0 */)
371 char buffer
[4096]; //@@@ auto-expand?
372 ssize_t length
= getAttr(name
, buffer
, sizeof(buffer
), 0, options
);
374 return string(buffer
, length
);
380 static bool checkFork(ssize_t rc
)
383 case 0: // empty fork, produced by NFS/AFP et al; ignore
385 default: // non-empty fork present, fail
387 case -1: // failed system call; let's see...
390 return false; // not present, no problem
392 return false; // HFS+ returns that if we ask for Resource Forks on anything but plain files (e.g. directories)
394 UnixError::throwMe();
399 bool filehasExtendedAttribute(const char *path
, const char *forkname
)
401 return checkFork(::getxattr(path
, forkname
, NULL
, 0, 0, 0));
404 bool FileDesc::hasExtendedAttribute(const char *forkname
) const
406 return checkFork(::fgetxattr(mFd
, forkname
, NULL
, 0, 0, 0));
409 bool FileDesc::isPlainFile(const std::string
&path
)
413 if (::lstat(path
.c_str(), &st2
))
414 UnixError::throwMe();
416 return (st1
.st_ino
== st2
.st_ino
&& S_ISREG(st2
.st_mode
));
422 void FileDesc::fstat(UnixStat
&st
) const
424 if (::fstat(mFd
, &st
))
425 UnixError::throwMe();
428 size_t FileDesc::fileSize() const
432 return (size_t)st
.st_size
;
435 bool FileDesc::isA(int mode
) const
439 return (st
.st_mode
& S_IFMT
) == mode
;
443 void FileDesc::chown(uid_t uid
)
445 checkError(::fchown(mFd
, uid
, gid_t(-1)));
448 void FileDesc::chown(uid_t uid
, gid_t gid
)
450 checkError(::fchown(mFd
, uid
, gid
));
453 void FileDesc::chgrp(gid_t gid
)
455 checkError(::fchown(mFd
, uid_t(-1), gid
));
458 void FileDesc::chmod(mode_t mode
)
460 checkError(::fchmod(mFd
, mode
));
463 void FileDesc::chflags(u_int flags
)
465 checkError(::fchflags(mFd
, flags
));
469 FILE *FileDesc::fdopen(const char *form
)
471 //@@@ pick default value for 'form' based on chracteristics of mFd
472 return ::fdopen(mFd
, form
);
475 AutoFileDesc::AutoFileDesc(const AutoFileDesc
& rhs
)
477 if (rhs
.fd() != invalidFd
) {
478 checkSetFd(::dup(rhs
.fd()));
485 // Device characteristics
487 static CFDictionaryRef CF_RETURNS_RETAINED
deviceCharacteristics(FileDesc
&fd
)
490 FileDesc::UnixStat st
;
492 CFTemp
<CFDictionaryRef
> matching("{%s=%d,%s=%d}",
493 kIOBSDMajorKey
, major(st
.st_dev
),
494 kIOBSDMinorKey
, minor(st
.st_dev
)
496 // IOServiceGetMatchingService CONSUMES its dictionary argument(!)
497 io_registry_entry_t entry
= IOServiceGetMatchingService(kIOMasterPortDefault
, matching
.yield());
498 if (entry
!= IO_OBJECT_NULL
) {
499 // get device characteristics
500 CFDictionaryRef characteristics
= (CFDictionaryRef
)IORegistryEntrySearchCFProperty(entry
,
502 CFSTR(kIOPropertyDeviceCharacteristicsKey
),
504 kIORegistryIterateRecursively
| kIORegistryIterateParents
);
505 IOObjectRelease(entry
);
506 return characteristics
;
509 return NULL
; // unable to get device characteristics
512 std::string
FileDesc::mediumType()
514 CFRef
<CFDictionaryRef
> characteristics
= deviceCharacteristics(*this);
515 if (characteristics
) {
516 CFStringRef mediumType
= (CFStringRef
)CFDictionaryGetValue(characteristics
, CFSTR(kIOPropertyMediumTypeKey
));
518 return cfString(mediumType
);
525 // Signals and signal masks
527 SigSet
sigMask(SigSet set
, int how
/* = SIG_SETMASK */)
530 checkError(::sigprocmask(how
, &set
.value(), &old
));
536 // Make or use a directory, open-style.
538 // Flags are to be interpreted like open(2) flags; particularly
539 // O_CREAT make the directory if not present
540 // O_EXCL fail if the directory is present
541 // Other open(2) flags are currently ignored.
543 // Yes, it's a function.
545 void makedir(const char *path
, int flags
, mode_t mode
)
548 if (!stat(path
, &st
)) {
550 UnixError::throwMe(EEXIST
);
551 if (!S_ISDIR(st
.st_mode
))
552 UnixError::throwMe(ENOTDIR
);
553 secinfo("makedir", "%s exists", path
);
558 if (errno
!= ENOENT
|| !(flags
& O_CREAT
))
559 UnixError::throwMe();
561 // ENOENT and creation enabled
562 if (::mkdir(path
, mode
)) {
563 if (errno
== EEXIST
&& !(flags
& O_EXCL
))
564 return; // fine (race condition, resolved)
565 UnixError::throwMe();
567 secinfo("makedir", "%s created", path
);
572 // Open, read/write, close a (small) file on disk
574 int ffprintf(const char *path
, int flags
, mode_t mode
, const char *format
, ...)
576 FileDesc
fd(path
, flags
, mode
);
577 FILE *f
= fd
.fdopen("w");
579 va_start(args
, format
);
580 int rc
= vfprintf(f
, format
, args
);
583 UnixError::throwMe();
587 int ffscanf(const char *path
, const char *format
, ...)
589 if (FILE *f
= fopen(path
, "r")) {
591 va_start(args
, format
);
592 int rc
= vfscanf(f
, format
, args
);
597 UnixError::throwMe();
601 } // end namespace IPPlusPlus
602 } // end namespace Security