]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_utilities/lib/unix++.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / unix++.cpp
CommitLineData
b1ab9ed8 1/*
fa7225c8 2 * Copyright (c) 2000-2001,2003-2004,2011-2012,2014-2016 Apple Inc. All Rights Reserved.
b1ab9ed8
A
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
25//
26// unix++ - C++ layer for basic UNIX facilities
27//
28#include "unix++.h"
d87e1158 29#include <security_utilities/cfutilities.h>
fa7225c8 30#include <security_utilities/cfmunge.h>
b1ab9ed8
A
31#include <security_utilities/memutils.h>
32#include <security_utilities/debugging.h>
d87e1158 33#include <sys/dirent.h>
b1ab9ed8
A
34#include <sys/xattr.h>
35#include <cstdarg>
d87e1158
A
36#include <IOKit/IOKitLib.h>
37#include <IOKit/IOKitKeys.h>
fa7225c8 38#include <IOKit/IOBSD.h>
d87e1158 39#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
b1ab9ed8
A
40
41
42namespace Security {
43namespace UnixPlusPlus {
44
45using LowLevelMemoryUtilities::increment;
46
47
48//
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.
51//
52void FileDesc::open(const char *path, int flags, mode_t mode)
53{
427c49bc
A
54 if ((mFd = ::open(path, flags, mode & ~S_IFMT)) == -1) {
55 if (errno == ENOENT && (mode & S_IFMT) == modeMissingOk) {
b1ab9ed8 56 return;
427c49bc 57 } else {
b1ab9ed8 58 UnixError::throwMe();
427c49bc
A
59 }
60 }
b1ab9ed8 61 mAtEnd = false;
fa7225c8 62 secinfo("unixio", "open(%s,0x%x,0x%x) = %d", path, flags, mode, mFd);
b1ab9ed8
A
63}
64
65void FileDesc::close()
66{
67 if (mFd >= 0) {
68 checkError(::close(mFd));
fa7225c8 69 secinfo("unixio", "close(%d)", mFd);
b1ab9ed8
A
70 mFd = invalidFd;
71 }
72}
73
866f8763
A
74void FileDesc::closeAndLog()
75{
76 int result = 0;
77 int retryCount = 2;
78 int error = 0;
79 if (mFd >= 0) {
80 while((result = ::close(mFd)) == -1 && retryCount) {
81 error = errno;
82 switch (error) {
83 case EINTR:
84 case EIO:
85 retryCount --;
86 break;
87 default:
88 secinfo("unixio", "close(%d) error %d", mFd, error);
89 retryCount = 0;
90 break;
91 }
92 }
93 secinfo("unixio", "close(%d) err: %d", mFd, error);
94 mFd = invalidFd;
95 }
96}
b1ab9ed8
A
97
98//
99// Filedescoid operations
100//
101size_t FileDesc::read(void *addr, size_t length)
102{
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
fa7225c8 106 secinfo("unixio", "%d zero read (ignored)", mFd);
b1ab9ed8
A
107 return 0;
108 }
109 mAtEnd = true;
fa7225c8 110 secinfo("unixio", "%d end of data", mFd);
b1ab9ed8
A
111 return 0;
112 case -1: // error
113 if (errno == EAGAIN)
114 return 0; // no data, unknown end-of-source status
115 UnixError::throwMe(); // throw error
116 default: // have data
117 return rc;
118 }
119}
120
121size_t FileDesc::write(const void *addr, size_t length)
122{
123 ssize_t rc = ::write(mFd, addr, length);
124 if (rc == -1) {
125 if (errno == EAGAIN)
126 return 0;
127 UnixError::throwMe();
128 }
129 return rc;
130}
131
132
133//
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.
137//
427c49bc 138size_t FileDesc::read(void *addr, size_t length, size_t position)
b1ab9ed8
A
139{
140 return checkError(::pread(mFd, addr, length, position));
141}
142
427c49bc 143size_t FileDesc::write(const void *addr, size_t length, size_t position)
b1ab9ed8
A
144{
145 return checkError(::pwrite(mFd, addr, length, position));
146}
147
148
149//
150// Waiting (repeating) I/O
151//
152size_t FileDesc::readAll(void *addr, size_t length)
153{
154 size_t total = 0;
155 while (length > 0 && !atEnd()) {
156 size_t size = read(addr, length);
157 addr = increment(addr, size);
158 length -= size;
159 total += size;
160 }
161 return total;
162}
163
164size_t FileDesc::readAll(string &value)
165{
166 string s;
167 while (!atEnd()) {
168 char buffer[256];
169 if (size_t size = read(buffer, sizeof(buffer))) {
170 s += string(buffer, size);
171 continue;
172 }
173 }
174 swap(value, s);
175 return value.length();
176}
177
178
179void FileDesc::writeAll(const void *addr, size_t length)
180{
181 while (length > 0) {
182 size_t size = write(addr, length);
183 addr = increment(addr, size);
184 length -= size;
185 }
186}
187
188
fa7225c8
A
189void FileDesc::truncate(size_t offset)
190{
191 UnixError::check(ftruncate(mFd, offset));
192}
193
194
b1ab9ed8
A
195//
196// Seeking
197//
427c49bc 198size_t FileDesc::seek(size_t position, int whence)
b1ab9ed8 199{
427c49bc 200 return (size_t)checkError(::lseek(mFd, position, whence));
b1ab9ed8
A
201}
202
203size_t FileDesc::position() const
204{
427c49bc 205 return (size_t)checkError(::lseek(mFd, 0, SEEK_CUR));
b1ab9ed8
A
206}
207
208
209//
210// Mmap support
211//
427c49bc 212void *FileDesc::mmap(int prot, size_t length, int flags, size_t offset, void *addr)
b1ab9ed8
A
213{
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();
219 return result;
220}
221
222
223//
224// Basic fcntl support
225//
226int FileDesc::fcntl(int cmd, void *arg) const
227{
228 int rc = ::fcntl(mFd, cmd, arg);
fa7225c8 229 secinfo("unixio", "%d fcntl(%d,%p) = %d", mFd, cmd, arg, rc);
b1ab9ed8
A
230 return checkError(rc);
231}
232
233
234//
235// Nice fcntl forms
236//
237void FileDesc::setFlag(int flag, bool on) const
238{
239 if (flag) { // if there's anything at all to do...
240 int oldFlags = flags();
241 flags(on ? (oldFlags | flag) : (oldFlags & ~flag));
242 }
243}
244
245
246//
247// Duplication operations
248//
249FileDesc FileDesc::dup() const
250{
251 return FileDesc(checkError(::dup(mFd)), atEnd());
252}
253
254FileDesc FileDesc::dup(int newFd) const
255{
256 return FileDesc(checkError(::dup2(mFd, newFd)), atEnd());
257}
258
259
260//
261// Advisory locking, fcntl style
262//
263void FileDesc::lock(int type, const Pos &pos)
264{
265 LockArgs args(type, pos);
266 IFDEBUG(args.debug(fd(), "lock"));
267 checkError(fcntl(F_SETLKW, &args));
268}
269
270bool FileDesc::tryLock(int type, const Pos &pos)
271{
272 LockArgs args(type, pos);
273 IFDEBUG(args.debug(fd(), "tryLock"));
274 try {
275 fcntl(F_SETLK, &args);
276 return true;
277 } catch (const UnixError &err) {
278 if (err.error == EAGAIN)
279 return false;
280 else
281 throw;
282 }
283}
284
285#if !defined(NDEBUG)
286
287void FileDesc::LockArgs::debug(int fd, const char *what)
288{
fa7225c8 289 secinfo("fdlock", "%d %s %s:%ld(%ld)", fd, what,
b1ab9ed8
A
290 (l_whence == SEEK_SET) ? "ABS" : (l_whence == SEEK_CUR) ? "REL" : "END",
291 long(l_start), long(l_len));
292}
293
294#endif //NDEBUG
295
296
297//
298// ioctl support
299//
300int FileDesc::ioctl(int cmd, void *arg) const
301{
302 int rc = ::ioctl(mFd, cmd, arg);
303 if (rc == -1)
304 UnixError::throwMe();
305 return rc;
306}
307
308
309//
310// Xattr support
311//
312void FileDesc::setAttr(const char *name, const void *value, size_t length,
313 u_int32_t position /* = 0 */, int options /* = 0 */)
314{
315 checkError(::fsetxattr(mFd, name, value, length, position, options));
316}
317
fa7225c8 318ssize_t FileDesc::getAttrLength(const char *name, int options)
b1ab9ed8 319{
fa7225c8 320 ssize_t rc = ::fgetxattr(mFd, name, NULL, 0, 0, options);
b1ab9ed8
A
321 if (rc == -1)
322 switch (errno) {
323 case ENOATTR:
324 return -1;
325 default:
326 UnixError::throwMe();
327 }
328 return rc;
329}
330
331ssize_t FileDesc::getAttr(const char *name, void *value, size_t length,
332 u_int32_t position /* = 0 */, int options /* = 0 */)
333{
334 ssize_t rc = ::fgetxattr(mFd, name, value, length, position, options);
335 if (rc == -1)
336 switch (errno) {
337 case ENOATTR:
338 return -1;
339 default:
340 UnixError::throwMe();
341 }
342 return rc;
343}
344
345void FileDesc::removeAttr(const char *name, int options /* = 0 */)
346{
347 if (::fremovexattr(mFd, name, options))
348 switch (errno) {
349 case ENOATTR:
350 if (!(options & XATTR_REPLACE)) // somewhat mis-using an API flag here...
351 return; // attribute not found; we'll call that okay
352 // fall through
353 default:
354 UnixError::throwMe();
355 }
356}
357
358size_t FileDesc::listAttr(char *value, size_t length, int options /* = 0 */)
359{
360 return checkError(::flistxattr(mFd, value, length, options));
361}
362
363
364void FileDesc::setAttr(const std::string &name, const std::string &value, int options /* = 0 */)
365{
366 return setAttr(name, value.c_str(), value.size(), 0, options);
367}
368
369std::string FileDesc::getAttr(const std::string &name, int options /* = 0 */)
370{
371 char buffer[4096]; //@@@ auto-expand?
372 ssize_t length = getAttr(name, buffer, sizeof(buffer), 0, options);
373 if (length >= 0)
374 return string(buffer, length);
375 else
376 return string();
377}
378
fa7225c8
A
379
380static bool checkFork(ssize_t rc)
381{
382 switch (rc) {
383 case 0: // empty fork, produced by NFS/AFP et al; ignore
384 return false;
385 default: // non-empty fork present, fail
386 return true;
387 case -1: // failed system call; let's see...
388 switch (errno) {
389 case ENOATTR:
390 return false; // not present, no problem
391 case EPERM:
392 return false; // HFS+ returns that if we ask for Resource Forks on anything but plain files (e.g. directories)
393 default:
394 UnixError::throwMe();
395 }
396 }
397}
398
399bool filehasExtendedAttribute(const char *path, const char *forkname)
400{
401 return checkFork(::getxattr(path, forkname, NULL, 0, 0, 0));
402}
403
404bool FileDesc::hasExtendedAttribute(const char *forkname) const
405{
406 return checkFork(::fgetxattr(mFd, forkname, NULL, 0, 0, 0));
407}
408
80e23899
A
409bool FileDesc::isPlainFile(const std::string &path)
410{
411 UnixStat st1, st2;
412 this->fstat(st1);
413 if (::lstat(path.c_str(), &st2))
414 UnixError::throwMe();
415
416 return (st1.st_ino == st2.st_ino && S_ISREG(st2.st_mode));
417}
418
b1ab9ed8
A
419//
420// Stat support
421//
422void FileDesc::fstat(UnixStat &st) const
423{
424 if (::fstat(mFd, &st))
425 UnixError::throwMe();
426}
427
428size_t FileDesc::fileSize() const
429{
430 struct stat st;
431 fstat(st);
427c49bc 432 return (size_t)st.st_size;
b1ab9ed8
A
433}
434
435bool FileDesc::isA(int mode) const
436{
437 struct stat st;
438 fstat(st);
439 return (st.st_mode & S_IFMT) == mode;
440}
441
442
443void FileDesc::chown(uid_t uid)
444{
445 checkError(::fchown(mFd, uid, gid_t(-1)));
446}
447
448void FileDesc::chown(uid_t uid, gid_t gid)
449{
450 checkError(::fchown(mFd, uid, gid));
451}
452
453void FileDesc::chgrp(gid_t gid)
454{
455 checkError(::fchown(mFd, uid_t(-1), gid));
456}
457
458void FileDesc::chmod(mode_t mode)
459{
460 checkError(::fchmod(mFd, mode));
461}
462
463void FileDesc::chflags(u_int flags)
464{
465 checkError(::fchflags(mFd, flags));
466}
467
468
469FILE *FileDesc::fdopen(const char *form)
470{
471 //@@@ pick default value for 'form' based on chracteristics of mFd
472 return ::fdopen(mFd, form);
473}
474
866f8763
A
475AutoFileDesc::AutoFileDesc(const AutoFileDesc& rhs)
476{
477 if (rhs.fd() != invalidFd) {
478 checkSetFd(::dup(rhs.fd()));
479 }
480 mAtEnd = rhs.mAtEnd;
481}
482
b1ab9ed8 483
d87e1158
A
484//
485// Device characteristics
486//
866f8763 487static CFDictionaryRef CF_RETURNS_RETAINED deviceCharacteristics(FileDesc &fd)
d87e1158
A
488{
489 // get device name
fa7225c8 490 FileDesc::UnixStat st;
d87e1158 491 fd.fstat(st);
fa7225c8
A
492 CFTemp<CFDictionaryRef> matching("{%s=%d,%s=%d}",
493 kIOBSDMajorKey, major(st.st_dev),
494 kIOBSDMinorKey, minor(st.st_dev)
495 );
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,
501 kIOServicePlane,
502 CFSTR(kIOPropertyDeviceCharacteristicsKey),
503 NULL,
504 kIORegistryIterateRecursively | kIORegistryIterateParents);
505 IOObjectRelease(entry);
506 return characteristics;
d87e1158
A
507 }
508
509 return NULL; // unable to get device characteristics
510}
511
512std::string FileDesc::mediumType()
513{
514 CFRef<CFDictionaryRef> characteristics = deviceCharacteristics(*this);
515 if (characteristics) {
516 CFStringRef mediumType = (CFStringRef)CFDictionaryGetValue(characteristics, CFSTR(kIOPropertyMediumTypeKey));
517 if (mediumType)
518 return cfString(mediumType);
519 }
520 return string();
521}
522
523
b1ab9ed8
A
524//
525// Signals and signal masks
526//
527SigSet sigMask(SigSet set, int how /* = SIG_SETMASK */)
528{
529 sigset_t old;
530 checkError(::sigprocmask(how, &set.value(), &old));
531 return old;
532}
533
534
535//
536// Make or use a directory, open-style.
537//
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.
542//
543// Yes, it's a function.
544//
545void makedir(const char *path, int flags, mode_t mode)
546{
547 struct stat st;
548 if (!stat(path, &st)) {
549 if (flags & O_EXCL)
550 UnixError::throwMe(EEXIST);
551 if (!S_ISDIR(st.st_mode))
552 UnixError::throwMe(ENOTDIR);
fa7225c8 553 secinfo("makedir", "%s exists", path);
b1ab9ed8
A
554 return;
555 }
556
557 // stat failed
558 if (errno != ENOENT || !(flags & O_CREAT))
559 UnixError::throwMe();
560
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();
566 }
fa7225c8 567 secinfo("makedir", "%s created", path);
b1ab9ed8
A
568}
569
570
571//
572// Open, read/write, close a (small) file on disk
573//
574int ffprintf(const char *path, int flags, mode_t mode, const char *format, ...)
575{
576 FileDesc fd(path, flags, mode);
577 FILE *f = fd.fdopen("w");
578 va_list args;
579 va_start(args, format);
580 int rc = vfprintf(f, format, args);
581 va_end(args);
582 if (fclose(f))
583 UnixError::throwMe();
584 return rc;
585}
586
587int ffscanf(const char *path, const char *format, ...)
588{
589 if (FILE *f = fopen(path, "r")) {
590 va_list args;
591 va_start(args, format);
592 int rc = vfscanf(f, format, args);
593 va_end(args);
594 if (!fclose(f))
595 return rc;
596 }
597 UnixError::throwMe();
598}
599
600
601} // end namespace IPPlusPlus
602} // end namespace Security