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