]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/unix++.cpp
Security-57740.31.2.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
75 //
76 // Filedescoid operations
77 //
78 size_t FileDesc::read(void *addr, size_t length)
79 {
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);
84 return 0;
85 }
86 mAtEnd = true;
87 secinfo("unixio", "%d end of data", mFd);
88 return 0;
89 case -1: // error
90 if (errno == EAGAIN)
91 return 0; // no data, unknown end-of-source status
92 UnixError::throwMe(); // throw error
93 default: // have data
94 return rc;
95 }
96 }
97
98 size_t FileDesc::write(const void *addr, size_t length)
99 {
100 ssize_t rc = ::write(mFd, addr, length);
101 if (rc == -1) {
102 if (errno == EAGAIN)
103 return 0;
104 UnixError::throwMe();
105 }
106 return rc;
107 }
108
109
110 //
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.
114 //
115 size_t FileDesc::read(void *addr, size_t length, size_t position)
116 {
117 return checkError(::pread(mFd, addr, length, position));
118 }
119
120 size_t FileDesc::write(const void *addr, size_t length, size_t position)
121 {
122 return checkError(::pwrite(mFd, addr, length, position));
123 }
124
125
126 //
127 // Waiting (repeating) I/O
128 //
129 size_t FileDesc::readAll(void *addr, size_t length)
130 {
131 size_t total = 0;
132 while (length > 0 && !atEnd()) {
133 size_t size = read(addr, length);
134 addr = increment(addr, size);
135 length -= size;
136 total += size;
137 }
138 return total;
139 }
140
141 size_t FileDesc::readAll(string &value)
142 {
143 string s;
144 while (!atEnd()) {
145 char buffer[256];
146 if (size_t size = read(buffer, sizeof(buffer))) {
147 s += string(buffer, size);
148 continue;
149 }
150 }
151 swap(value, s);
152 return value.length();
153 }
154
155
156 void FileDesc::writeAll(const void *addr, size_t length)
157 {
158 while (length > 0) {
159 size_t size = write(addr, length);
160 addr = increment(addr, size);
161 length -= size;
162 }
163 }
164
165
166 void FileDesc::truncate(size_t offset)
167 {
168 UnixError::check(ftruncate(mFd, offset));
169 }
170
171
172 //
173 // Seeking
174 //
175 size_t FileDesc::seek(size_t position, int whence)
176 {
177 return (size_t)checkError(::lseek(mFd, position, whence));
178 }
179
180 size_t FileDesc::position() const
181 {
182 return (size_t)checkError(::lseek(mFd, 0, SEEK_CUR));
183 }
184
185
186 //
187 // Mmap support
188 //
189 void *FileDesc::mmap(int prot, size_t length, int flags, size_t offset, void *addr)
190 {
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();
196 return result;
197 }
198
199
200 //
201 // Basic fcntl support
202 //
203 int FileDesc::fcntl(int cmd, void *arg) const
204 {
205 int rc = ::fcntl(mFd, cmd, arg);
206 secinfo("unixio", "%d fcntl(%d,%p) = %d", mFd, cmd, arg, rc);
207 return checkError(rc);
208 }
209
210
211 //
212 // Nice fcntl forms
213 //
214 void FileDesc::setFlag(int flag, bool on) const
215 {
216 if (flag) { // if there's anything at all to do...
217 int oldFlags = flags();
218 flags(on ? (oldFlags | flag) : (oldFlags & ~flag));
219 }
220 }
221
222
223 //
224 // Duplication operations
225 //
226 FileDesc FileDesc::dup() const
227 {
228 return FileDesc(checkError(::dup(mFd)), atEnd());
229 }
230
231 FileDesc FileDesc::dup(int newFd) const
232 {
233 return FileDesc(checkError(::dup2(mFd, newFd)), atEnd());
234 }
235
236
237 //
238 // Advisory locking, fcntl style
239 //
240 void FileDesc::lock(int type, const Pos &pos)
241 {
242 LockArgs args(type, pos);
243 IFDEBUG(args.debug(fd(), "lock"));
244 checkError(fcntl(F_SETLKW, &args));
245 }
246
247 bool FileDesc::tryLock(int type, const Pos &pos)
248 {
249 LockArgs args(type, pos);
250 IFDEBUG(args.debug(fd(), "tryLock"));
251 try {
252 fcntl(F_SETLK, &args);
253 return true;
254 } catch (const UnixError &err) {
255 if (err.error == EAGAIN)
256 return false;
257 else
258 throw;
259 }
260 }
261
262 #if !defined(NDEBUG)
263
264 void FileDesc::LockArgs::debug(int fd, const char *what)
265 {
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));
269 }
270
271 #endif //NDEBUG
272
273
274 //
275 // ioctl support
276 //
277 int FileDesc::ioctl(int cmd, void *arg) const
278 {
279 int rc = ::ioctl(mFd, cmd, arg);
280 if (rc == -1)
281 UnixError::throwMe();
282 return rc;
283 }
284
285
286 //
287 // Xattr support
288 //
289 void FileDesc::setAttr(const char *name, const void *value, size_t length,
290 u_int32_t position /* = 0 */, int options /* = 0 */)
291 {
292 checkError(::fsetxattr(mFd, name, value, length, position, options));
293 }
294
295 ssize_t FileDesc::getAttrLength(const char *name, int options)
296 {
297 ssize_t rc = ::fgetxattr(mFd, name, NULL, 0, 0, options);
298 if (rc == -1)
299 switch (errno) {
300 case ENOATTR:
301 return -1;
302 default:
303 UnixError::throwMe();
304 }
305 return rc;
306 }
307
308 ssize_t FileDesc::getAttr(const char *name, void *value, size_t length,
309 u_int32_t position /* = 0 */, int options /* = 0 */)
310 {
311 ssize_t rc = ::fgetxattr(mFd, name, value, length, position, options);
312 if (rc == -1)
313 switch (errno) {
314 case ENOATTR:
315 return -1;
316 default:
317 UnixError::throwMe();
318 }
319 return rc;
320 }
321
322 void FileDesc::removeAttr(const char *name, int options /* = 0 */)
323 {
324 if (::fremovexattr(mFd, name, options))
325 switch (errno) {
326 case ENOATTR:
327 if (!(options & XATTR_REPLACE)) // somewhat mis-using an API flag here...
328 return; // attribute not found; we'll call that okay
329 // fall through
330 default:
331 UnixError::throwMe();
332 }
333 }
334
335 size_t FileDesc::listAttr(char *value, size_t length, int options /* = 0 */)
336 {
337 return checkError(::flistxattr(mFd, value, length, options));
338 }
339
340
341 void FileDesc::setAttr(const std::string &name, const std::string &value, int options /* = 0 */)
342 {
343 return setAttr(name, value.c_str(), value.size(), 0, options);
344 }
345
346 std::string FileDesc::getAttr(const std::string &name, int options /* = 0 */)
347 {
348 char buffer[4096]; //@@@ auto-expand?
349 ssize_t length = getAttr(name, buffer, sizeof(buffer), 0, options);
350 if (length >= 0)
351 return string(buffer, length);
352 else
353 return string();
354 }
355
356
357 static bool checkFork(ssize_t rc)
358 {
359 switch (rc) {
360 case 0: // empty fork, produced by NFS/AFP et al; ignore
361 return false;
362 default: // non-empty fork present, fail
363 return true;
364 case -1: // failed system call; let's see...
365 switch (errno) {
366 case ENOATTR:
367 return false; // not present, no problem
368 case EPERM:
369 return false; // HFS+ returns that if we ask for Resource Forks on anything but plain files (e.g. directories)
370 default:
371 UnixError::throwMe();
372 }
373 }
374 }
375
376 bool filehasExtendedAttribute(const char *path, const char *forkname)
377 {
378 return checkFork(::getxattr(path, forkname, NULL, 0, 0, 0));
379 }
380
381 bool FileDesc::hasExtendedAttribute(const char *forkname) const
382 {
383 return checkFork(::fgetxattr(mFd, forkname, NULL, 0, 0, 0));
384 }
385
386 bool FileDesc::isPlainFile(const std::string &path)
387 {
388 UnixStat st1, st2;
389 this->fstat(st1);
390 if (::lstat(path.c_str(), &st2))
391 UnixError::throwMe();
392
393 return (st1.st_ino == st2.st_ino && S_ISREG(st2.st_mode));
394 }
395
396 //
397 // Stat support
398 //
399 void FileDesc::fstat(UnixStat &st) const
400 {
401 if (::fstat(mFd, &st))
402 UnixError::throwMe();
403 }
404
405 size_t FileDesc::fileSize() const
406 {
407 struct stat st;
408 fstat(st);
409 return (size_t)st.st_size;
410 }
411
412 bool FileDesc::isA(int mode) const
413 {
414 struct stat st;
415 fstat(st);
416 return (st.st_mode & S_IFMT) == mode;
417 }
418
419
420 void FileDesc::chown(uid_t uid)
421 {
422 checkError(::fchown(mFd, uid, gid_t(-1)));
423 }
424
425 void FileDesc::chown(uid_t uid, gid_t gid)
426 {
427 checkError(::fchown(mFd, uid, gid));
428 }
429
430 void FileDesc::chgrp(gid_t gid)
431 {
432 checkError(::fchown(mFd, uid_t(-1), gid));
433 }
434
435 void FileDesc::chmod(mode_t mode)
436 {
437 checkError(::fchmod(mFd, mode));
438 }
439
440 void FileDesc::chflags(u_int flags)
441 {
442 checkError(::fchflags(mFd, flags));
443 }
444
445
446 FILE *FileDesc::fdopen(const char *form)
447 {
448 //@@@ pick default value for 'form' based on chracteristics of mFd
449 return ::fdopen(mFd, form);
450 }
451
452
453 //
454 // Device characteristics
455 //
456 static CFDictionaryRef deviceCharacteristics(FileDesc &fd)
457 {
458 // get device name
459 FileDesc::UnixStat st;
460 fd.fstat(st);
461 CFTemp<CFDictionaryRef> matching("{%s=%d,%s=%d}",
462 kIOBSDMajorKey, major(st.st_dev),
463 kIOBSDMinorKey, minor(st.st_dev)
464 );
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,
470 kIOServicePlane,
471 CFSTR(kIOPropertyDeviceCharacteristicsKey),
472 NULL,
473 kIORegistryIterateRecursively | kIORegistryIterateParents);
474 IOObjectRelease(entry);
475 return characteristics;
476 }
477
478 return NULL; // unable to get device characteristics
479 }
480
481 std::string FileDesc::mediumType()
482 {
483 CFRef<CFDictionaryRef> characteristics = deviceCharacteristics(*this);
484 if (characteristics) {
485 CFStringRef mediumType = (CFStringRef)CFDictionaryGetValue(characteristics, CFSTR(kIOPropertyMediumTypeKey));
486 if (mediumType)
487 return cfString(mediumType);
488 }
489 return string();
490 }
491
492
493 //
494 // Signals and signal masks
495 //
496 SigSet sigMask(SigSet set, int how /* = SIG_SETMASK */)
497 {
498 sigset_t old;
499 checkError(::sigprocmask(how, &set.value(), &old));
500 return old;
501 }
502
503
504 //
505 // Make or use a directory, open-style.
506 //
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.
511 //
512 // Yes, it's a function.
513 //
514 void makedir(const char *path, int flags, mode_t mode)
515 {
516 struct stat st;
517 if (!stat(path, &st)) {
518 if (flags & O_EXCL)
519 UnixError::throwMe(EEXIST);
520 if (!S_ISDIR(st.st_mode))
521 UnixError::throwMe(ENOTDIR);
522 secinfo("makedir", "%s exists", path);
523 return;
524 }
525
526 // stat failed
527 if (errno != ENOENT || !(flags & O_CREAT))
528 UnixError::throwMe();
529
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();
535 }
536 secinfo("makedir", "%s created", path);
537 }
538
539
540 //
541 // Open, read/write, close a (small) file on disk
542 //
543 int ffprintf(const char *path, int flags, mode_t mode, const char *format, ...)
544 {
545 FileDesc fd(path, flags, mode);
546 FILE *f = fd.fdopen("w");
547 va_list args;
548 va_start(args, format);
549 int rc = vfprintf(f, format, args);
550 va_end(args);
551 if (fclose(f))
552 UnixError::throwMe();
553 return rc;
554 }
555
556 int ffscanf(const char *path, const char *format, ...)
557 {
558 if (FILE *f = fopen(path, "r")) {
559 va_list args;
560 va_start(args, format);
561 int rc = vfscanf(f, format, args);
562 va_end(args);
563 if (!fclose(f))
564 return rc;
565 }
566 UnixError::throwMe();
567 }
568
569
570 } // end namespace IPPlusPlus
571 } // end namespace Security