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