]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/unix++.cpp
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / unix++.cpp
1 /*
2 * Copyright (c) 2000-2001,2003-2004,2011-2012,2014-2016 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
25 //
26 // unix++ - C++ layer for basic UNIX facilities
27 //
28 #include "unix++.h"
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>
35 #include <cstdarg>
36 #include <IOKit/IOKitLib.h>
37 #include <IOKit/IOKitKeys.h>
38 #include <IOKit/IOBSD.h>
39 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
40
41
42 namespace Security {
43 namespace UnixPlusPlus {
44
45 using 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 //
52 void FileDesc::open(const char *path, int flags, mode_t mode)
53 {
54 if ((mFd = ::open(path, flags, mode & ~S_IFMT)) == -1) {
55 if (errno == ENOENT && (mode & S_IFMT) == modeMissingOk) {
56 return;
57 } else {
58 UnixError::throwMe();
59 }
60 }
61 mAtEnd = false;
62 secinfo("unixio", "open(%s,0x%x,0x%x) = %d", path, flags, mode, mFd);
63 }
64
65 void FileDesc::close()
66 {
67 if (mFd >= 0) {
68 checkError(::close(mFd));
69 secinfo("unixio", "close(%d)", mFd);
70 mFd = invalidFd;
71 }
72 }
73
74 void 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 }
97
98 //
99 // Filedescoid operations
100 //
101 size_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
106 secinfo("unixio", "%d zero read (ignored)", mFd);
107 return 0;
108 }
109 mAtEnd = true;
110 secinfo("unixio", "%d end of data", mFd);
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
121 size_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 //
138 size_t FileDesc::read(void *addr, size_t length, size_t position)
139 {
140 return checkError(::pread(mFd, addr, length, position));
141 }
142
143 size_t FileDesc::write(const void *addr, size_t length, size_t position)
144 {
145 return checkError(::pwrite(mFd, addr, length, position));
146 }
147
148
149 //
150 // Waiting (repeating) I/O
151 //
152 size_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
164 size_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
179 void 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
189 void FileDesc::truncate(size_t offset)
190 {
191 UnixError::check(ftruncate(mFd, offset));
192 }
193
194
195 //
196 // Seeking
197 //
198 size_t FileDesc::seek(size_t position, int whence)
199 {
200 return (size_t)checkError(::lseek(mFd, position, whence));
201 }
202
203 size_t FileDesc::position() const
204 {
205 return (size_t)checkError(::lseek(mFd, 0, SEEK_CUR));
206 }
207
208
209 //
210 // Mmap support
211 //
212 void *FileDesc::mmap(int prot, size_t length, int flags, size_t offset, void *addr)
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 //
226 int FileDesc::fcntl(int cmd, void *arg) const
227 {
228 int rc = ::fcntl(mFd, cmd, arg);
229 secinfo("unixio", "%d fcntl(%d,%p) = %d", mFd, cmd, arg, rc);
230 return checkError(rc);
231 }
232
233
234 //
235 // Nice fcntl forms
236 //
237 void 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 //
249 FileDesc FileDesc::dup() const
250 {
251 return FileDesc(checkError(::dup(mFd)), atEnd());
252 }
253
254 FileDesc FileDesc::dup(int newFd) const
255 {
256 return FileDesc(checkError(::dup2(mFd, newFd)), atEnd());
257 }
258
259
260 //
261 // Advisory locking, fcntl style
262 //
263 void 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
270 bool 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
287 void FileDesc::LockArgs::debug(int fd, const char *what)
288 {
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));
292 }
293
294 #endif //NDEBUG
295
296
297 //
298 // ioctl support
299 //
300 int 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 //
312 void 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
318 ssize_t FileDesc::getAttrLength(const char *name, int options)
319 {
320 ssize_t rc = ::fgetxattr(mFd, name, NULL, 0, 0, options);
321 if (rc == -1)
322 switch (errno) {
323 case ENOATTR:
324 return -1;
325 default:
326 UnixError::throwMe();
327 }
328 return rc;
329 }
330
331 ssize_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
345 void 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
358 size_t FileDesc::listAttr(char *value, size_t length, int options /* = 0 */)
359 {
360 return checkError(::flistxattr(mFd, value, length, options));
361 }
362
363
364 void 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
369 std::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
379
380 static 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
399 bool filehasExtendedAttribute(const char *path, const char *forkname)
400 {
401 return checkFork(::getxattr(path, forkname, NULL, 0, 0, 0));
402 }
403
404 bool FileDesc::hasExtendedAttribute(const char *forkname) const
405 {
406 return checkFork(::fgetxattr(mFd, forkname, NULL, 0, 0, 0));
407 }
408
409 bool 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
419 //
420 // Stat support
421 //
422 void FileDesc::fstat(UnixStat &st) const
423 {
424 if (::fstat(mFd, &st))
425 UnixError::throwMe();
426 }
427
428 size_t FileDesc::fileSize() const
429 {
430 struct stat st;
431 fstat(st);
432 return (size_t)st.st_size;
433 }
434
435 bool FileDesc::isA(int mode) const
436 {
437 struct stat st;
438 fstat(st);
439 return (st.st_mode & S_IFMT) == mode;
440 }
441
442
443 void FileDesc::chown(uid_t uid)
444 {
445 checkError(::fchown(mFd, uid, gid_t(-1)));
446 }
447
448 void FileDesc::chown(uid_t uid, gid_t gid)
449 {
450 checkError(::fchown(mFd, uid, gid));
451 }
452
453 void FileDesc::chgrp(gid_t gid)
454 {
455 checkError(::fchown(mFd, uid_t(-1), gid));
456 }
457
458 void FileDesc::chmod(mode_t mode)
459 {
460 checkError(::fchmod(mFd, mode));
461 }
462
463 void FileDesc::chflags(u_int flags)
464 {
465 checkError(::fchflags(mFd, flags));
466 }
467
468
469 FILE *FileDesc::fdopen(const char *form)
470 {
471 //@@@ pick default value for 'form' based on chracteristics of mFd
472 return ::fdopen(mFd, form);
473 }
474
475 AutoFileDesc::AutoFileDesc(const AutoFileDesc& rhs)
476 {
477 if (rhs.fd() != invalidFd) {
478 checkSetFd(::dup(rhs.fd()));
479 }
480 mAtEnd = rhs.mAtEnd;
481 }
482
483
484 //
485 // Device characteristics
486 //
487 static CFDictionaryRef CF_RETURNS_RETAINED deviceCharacteristics(FileDesc &fd)
488 {
489 // get device name
490 FileDesc::UnixStat st;
491 fd.fstat(st);
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;
507 }
508
509 return NULL; // unable to get device characteristics
510 }
511
512 std::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
524 //
525 // Signals and signal masks
526 //
527 SigSet 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 //
545 void 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);
553 secinfo("makedir", "%s exists", path);
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 }
567 secinfo("makedir", "%s created", path);
568 }
569
570
571 //
572 // Open, read/write, close a (small) file on disk
573 //
574 int 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
587 int 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