]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/unix++.cpp
Security-57337.20.44.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / unix++.cpp
1 /*
2 * Copyright (c) 2000-2001,2003-2004,2011-2012,2014 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/memutils.h>
31 #include <security_utilities/debugging.h>
32 #include <sys/dirent.h>
33 #include <sys/xattr.h>
34 #include <cstdarg>
35 #include <IOKit/IOKitLib.h>
36 #include <IOKit/IOKitKeys.h>
37 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
38
39
40 namespace Security {
41 namespace UnixPlusPlus {
42
43 using LowLevelMemoryUtilities::increment;
44
45
46 //
47 // Canonical open of a file descriptor. All other open operations channel through this.
48 // Note that we abuse the S_IFMT mode flags as operational options.
49 //
50 void FileDesc::open(const char *path, int flags, mode_t mode)
51 {
52 if ((mFd = ::open(path, flags, mode & ~S_IFMT)) == -1) {
53 if (errno == ENOENT && (mode & S_IFMT) == modeMissingOk) {
54 return;
55 } else {
56 UnixError::throwMe();
57 }
58 }
59 mAtEnd = false;
60 secdebug("unixio", "open(%s,0x%x,0x%x) = %d", path, flags, mode, mFd);
61 }
62
63 void FileDesc::close()
64 {
65 if (mFd >= 0) {
66 checkError(::close(mFd));
67 secdebug("unixio", "close(%d)", mFd);
68 mFd = invalidFd;
69 }
70 }
71
72
73 //
74 // Filedescoid operations
75 //
76 size_t FileDesc::read(void *addr, size_t length)
77 {
78 switch (ssize_t rc = ::read(mFd, addr, length)) {
79 case 0: // end-of-source
80 if (length == 0) { // check for errors, but don't set mAtEnd unless we have to
81 secdebug("unixio", "%d zero read (ignored)", mFd);
82 return 0;
83 }
84 mAtEnd = true;
85 secdebug("unixio", "%d end of data", mFd);
86 return 0;
87 case -1: // error
88 if (errno == EAGAIN)
89 return 0; // no data, unknown end-of-source status
90 UnixError::throwMe(); // throw error
91 default: // have data
92 return rc;
93 }
94 }
95
96 size_t FileDesc::write(const void *addr, size_t length)
97 {
98 ssize_t rc = ::write(mFd, addr, length);
99 if (rc == -1) {
100 if (errno == EAGAIN)
101 return 0;
102 UnixError::throwMe();
103 }
104 return rc;
105 }
106
107
108 //
109 // I/O with integral positioning.
110 // These don't affect file position and the atEnd() flag; and they
111 // don't make allowances for asynchronous I/O.
112 //
113 size_t FileDesc::read(void *addr, size_t length, size_t position)
114 {
115 return checkError(::pread(mFd, addr, length, position));
116 }
117
118 size_t FileDesc::write(const void *addr, size_t length, size_t position)
119 {
120 return checkError(::pwrite(mFd, addr, length, position));
121 }
122
123
124 //
125 // Waiting (repeating) I/O
126 //
127 size_t FileDesc::readAll(void *addr, size_t length)
128 {
129 size_t total = 0;
130 while (length > 0 && !atEnd()) {
131 size_t size = read(addr, length);
132 addr = increment(addr, size);
133 length -= size;
134 total += size;
135 }
136 return total;
137 }
138
139 size_t FileDesc::readAll(string &value)
140 {
141 string s;
142 while (!atEnd()) {
143 char buffer[256];
144 if (size_t size = read(buffer, sizeof(buffer))) {
145 s += string(buffer, size);
146 continue;
147 }
148 }
149 swap(value, s);
150 return value.length();
151 }
152
153
154 void FileDesc::writeAll(const void *addr, size_t length)
155 {
156 while (length > 0) {
157 size_t size = write(addr, length);
158 addr = increment(addr, size);
159 length -= size;
160 }
161 }
162
163
164 //
165 // Seeking
166 //
167 size_t FileDesc::seek(size_t position, int whence)
168 {
169 return (size_t)checkError(::lseek(mFd, position, whence));
170 }
171
172 size_t FileDesc::position() const
173 {
174 return (size_t)checkError(::lseek(mFd, 0, SEEK_CUR));
175 }
176
177
178 //
179 // Mmap support
180 //
181 void *FileDesc::mmap(int prot, size_t length, int flags, size_t offset, void *addr)
182 {
183 if (!(flags & (MAP_PRIVATE | MAP_SHARED))) // one is required
184 flags |= MAP_PRIVATE;
185 void *result = ::mmap(addr, length ? length : fileSize(), prot, flags, mFd, offset);
186 if (result == MAP_FAILED)
187 UnixError::throwMe();
188 return result;
189 }
190
191
192 //
193 // Basic fcntl support
194 //
195 int FileDesc::fcntl(int cmd, void *arg) const
196 {
197 int rc = ::fcntl(mFd, cmd, arg);
198 secdebug("unixio", "%d fcntl(%d,%p) = %d", mFd, cmd, arg, rc);
199 return checkError(rc);
200 }
201
202
203 //
204 // Nice fcntl forms
205 //
206 void FileDesc::setFlag(int flag, bool on) const
207 {
208 if (flag) { // if there's anything at all to do...
209 int oldFlags = flags();
210 flags(on ? (oldFlags | flag) : (oldFlags & ~flag));
211 }
212 }
213
214
215 //
216 // Duplication operations
217 //
218 FileDesc FileDesc::dup() const
219 {
220 return FileDesc(checkError(::dup(mFd)), atEnd());
221 }
222
223 FileDesc FileDesc::dup(int newFd) const
224 {
225 return FileDesc(checkError(::dup2(mFd, newFd)), atEnd());
226 }
227
228
229 //
230 // Advisory locking, fcntl style
231 //
232 void FileDesc::lock(int type, const Pos &pos)
233 {
234 LockArgs args(type, pos);
235 IFDEBUG(args.debug(fd(), "lock"));
236 checkError(fcntl(F_SETLKW, &args));
237 }
238
239 bool FileDesc::tryLock(int type, const Pos &pos)
240 {
241 LockArgs args(type, pos);
242 IFDEBUG(args.debug(fd(), "tryLock"));
243 try {
244 fcntl(F_SETLK, &args);
245 return true;
246 } catch (const UnixError &err) {
247 if (err.error == EAGAIN)
248 return false;
249 else
250 throw;
251 }
252 }
253
254 #if !defined(NDEBUG)
255
256 void FileDesc::LockArgs::debug(int fd, const char *what)
257 {
258 secdebug("fdlock", "%d %s %s:%ld(%ld)", fd, what,
259 (l_whence == SEEK_SET) ? "ABS" : (l_whence == SEEK_CUR) ? "REL" : "END",
260 long(l_start), long(l_len));
261 }
262
263 #endif //NDEBUG
264
265
266 //
267 // ioctl support
268 //
269 int FileDesc::ioctl(int cmd, void *arg) const
270 {
271 int rc = ::ioctl(mFd, cmd, arg);
272 if (rc == -1)
273 UnixError::throwMe();
274 return rc;
275 }
276
277
278 //
279 // Xattr support
280 //
281 void FileDesc::setAttr(const char *name, const void *value, size_t length,
282 u_int32_t position /* = 0 */, int options /* = 0 */)
283 {
284 checkError(::fsetxattr(mFd, name, value, length, position, options));
285 }
286
287 ssize_t FileDesc::getAttrLength(const char *name)
288 {
289 ssize_t rc = ::fgetxattr(mFd, name, NULL, 0, 0, 0);
290 if (rc == -1)
291 switch (errno) {
292 case ENOATTR:
293 return -1;
294 default:
295 UnixError::throwMe();
296 }
297 return rc;
298 }
299
300 ssize_t FileDesc::getAttr(const char *name, void *value, size_t length,
301 u_int32_t position /* = 0 */, int options /* = 0 */)
302 {
303 ssize_t rc = ::fgetxattr(mFd, name, value, length, position, options);
304 if (rc == -1)
305 switch (errno) {
306 case ENOATTR:
307 return -1;
308 default:
309 UnixError::throwMe();
310 }
311 return rc;
312 }
313
314 void FileDesc::removeAttr(const char *name, int options /* = 0 */)
315 {
316 if (::fremovexattr(mFd, name, options))
317 switch (errno) {
318 case ENOATTR:
319 if (!(options & XATTR_REPLACE)) // somewhat mis-using an API flag here...
320 return; // attribute not found; we'll call that okay
321 // fall through
322 default:
323 UnixError::throwMe();
324 }
325 }
326
327 size_t FileDesc::listAttr(char *value, size_t length, int options /* = 0 */)
328 {
329 return checkError(::flistxattr(mFd, value, length, options));
330 }
331
332
333 void FileDesc::setAttr(const std::string &name, const std::string &value, int options /* = 0 */)
334 {
335 return setAttr(name, value.c_str(), value.size(), 0, options);
336 }
337
338 std::string FileDesc::getAttr(const std::string &name, int options /* = 0 */)
339 {
340 char buffer[4096]; //@@@ auto-expand?
341 ssize_t length = getAttr(name, buffer, sizeof(buffer), 0, options);
342 if (length >= 0)
343 return string(buffer, length);
344 else
345 return string();
346 }
347
348 bool FileDesc::isPlainFile(const std::string &path)
349 {
350 UnixStat st1, st2;
351 this->fstat(st1);
352 if (::lstat(path.c_str(), &st2))
353 UnixError::throwMe();
354
355 return (st1.st_ino == st2.st_ino && S_ISREG(st2.st_mode));
356 }
357
358 //
359 // Stat support
360 //
361 void FileDesc::fstat(UnixStat &st) const
362 {
363 if (::fstat(mFd, &st))
364 UnixError::throwMe();
365 }
366
367 size_t FileDesc::fileSize() const
368 {
369 struct stat st;
370 fstat(st);
371 return (size_t)st.st_size;
372 }
373
374 bool FileDesc::isA(int mode) const
375 {
376 struct stat st;
377 fstat(st);
378 return (st.st_mode & S_IFMT) == mode;
379 }
380
381
382 void FileDesc::chown(uid_t uid)
383 {
384 checkError(::fchown(mFd, uid, gid_t(-1)));
385 }
386
387 void FileDesc::chown(uid_t uid, gid_t gid)
388 {
389 checkError(::fchown(mFd, uid, gid));
390 }
391
392 void FileDesc::chgrp(gid_t gid)
393 {
394 checkError(::fchown(mFd, uid_t(-1), gid));
395 }
396
397 void FileDesc::chmod(mode_t mode)
398 {
399 checkError(::fchmod(mFd, mode));
400 }
401
402 void FileDesc::chflags(u_int flags)
403 {
404 checkError(::fchflags(mFd, flags));
405 }
406
407
408 FILE *FileDesc::fdopen(const char *form)
409 {
410 //@@@ pick default value for 'form' based on chracteristics of mFd
411 return ::fdopen(mFd, form);
412 }
413
414
415 //
416 // Device characteristics
417 //
418 static CFDictionaryRef deviceCharacteristics(FileDesc &fd)
419 {
420 // get device name
421 AutoFileDesc::UnixStat st;
422 fd.fstat(st);
423 char buffer[MAXNAMLEN];
424 checkError(::devname_r(st.st_dev, S_IFBLK, buffer, MAXNAMLEN));
425
426 // search in IO Registry for named device
427 CFDictionaryRef matching = IOBSDNameMatching(kIOMasterPortDefault, 0, buffer);
428 if (matching) {
429 // fetch the object with the matching BSD name (consumes reference on matching)
430 io_registry_entry_t entry = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
431 if (entry != IO_OBJECT_NULL) {
432 // get device characteristics
433 CFDictionaryRef characteristics = (CFDictionaryRef)IORegistryEntrySearchCFProperty(entry,
434 kIOServicePlane,
435 CFSTR(kIOPropertyDeviceCharacteristicsKey),
436 NULL,
437 kIORegistryIterateRecursively | kIORegistryIterateParents);
438 IOObjectRelease(entry);
439 return characteristics;
440 }
441 }
442
443 return NULL; // unable to get device characteristics
444 }
445
446 std::string FileDesc::mediumType()
447 {
448 CFRef<CFDictionaryRef> characteristics = deviceCharacteristics(*this);
449 if (characteristics) {
450 CFStringRef mediumType = (CFStringRef)CFDictionaryGetValue(characteristics, CFSTR(kIOPropertyMediumTypeKey));
451 if (mediumType)
452 return cfString(mediumType);
453 }
454 return string();
455 }
456
457
458 //
459 // Signals and signal masks
460 //
461 SigSet sigMask(SigSet set, int how /* = SIG_SETMASK */)
462 {
463 sigset_t old;
464 checkError(::sigprocmask(how, &set.value(), &old));
465 return old;
466 }
467
468
469 //
470 // Make or use a directory, open-style.
471 //
472 // Flags are to be interpreted like open(2) flags; particularly
473 // O_CREAT make the directory if not present
474 // O_EXCL fail if the directory is present
475 // Other open(2) flags are currently ignored.
476 //
477 // Yes, it's a function.
478 //
479 void makedir(const char *path, int flags, mode_t mode)
480 {
481 struct stat st;
482 if (!stat(path, &st)) {
483 if (flags & O_EXCL)
484 UnixError::throwMe(EEXIST);
485 if (!S_ISDIR(st.st_mode))
486 UnixError::throwMe(ENOTDIR);
487 secdebug("makedir", "%s exists", path);
488 return;
489 }
490
491 // stat failed
492 if (errno != ENOENT || !(flags & O_CREAT))
493 UnixError::throwMe();
494
495 // ENOENT and creation enabled
496 if (::mkdir(path, mode)) {
497 if (errno == EEXIST && !(flags & O_EXCL))
498 return; // fine (race condition, resolved)
499 UnixError::throwMe();
500 }
501 secdebug("makedir", "%s created", path);
502 }
503
504
505 //
506 // Open, read/write, close a (small) file on disk
507 //
508 int ffprintf(const char *path, int flags, mode_t mode, const char *format, ...)
509 {
510 FileDesc fd(path, flags, mode);
511 FILE *f = fd.fdopen("w");
512 va_list args;
513 va_start(args, format);
514 int rc = vfprintf(f, format, args);
515 va_end(args);
516 if (fclose(f))
517 UnixError::throwMe();
518 return rc;
519 }
520
521 int ffscanf(const char *path, const char *format, ...)
522 {
523 if (FILE *f = fopen(path, "r")) {
524 va_list args;
525 va_start(args, format);
526 int rc = vfscanf(f, format, args);
527 va_end(args);
528 if (!fclose(f))
529 return rc;
530 }
531 UnixError::throwMe();
532 }
533
534
535 } // end namespace IPPlusPlus
536 } // end namespace Security