]> git.saurik.com Git - apple/security.git/blob - libsecurity_utilities/lib/unix++.cpp
Security-55471.14.8.tar.gz
[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 }
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
346 //
347 // Stat support
348 //
349 void FileDesc::fstat(UnixStat &st) const
350 {
351 if (::fstat(mFd, &st))
352 UnixError::throwMe();
353 }
354
355 size_t FileDesc::fileSize() const
356 {
357 struct stat st;
358 fstat(st);
359 return (size_t)st.st_size;
360 }
361
362 bool FileDesc::isA(int mode) const
363 {
364 struct stat st;
365 fstat(st);
366 return (st.st_mode & S_IFMT) == mode;
367 }
368
369
370 void FileDesc::chown(uid_t uid)
371 {
372 checkError(::fchown(mFd, uid, gid_t(-1)));
373 }
374
375 void FileDesc::chown(uid_t uid, gid_t gid)
376 {
377 checkError(::fchown(mFd, uid, gid));
378 }
379
380 void FileDesc::chgrp(gid_t gid)
381 {
382 checkError(::fchown(mFd, uid_t(-1), gid));
383 }
384
385 void FileDesc::chmod(mode_t mode)
386 {
387 checkError(::fchmod(mFd, mode));
388 }
389
390 void FileDesc::chflags(u_int flags)
391 {
392 checkError(::fchflags(mFd, flags));
393 }
394
395
396 FILE *FileDesc::fdopen(const char *form)
397 {
398 //@@@ pick default value for 'form' based on chracteristics of mFd
399 return ::fdopen(mFd, form);
400 }
401
402
403 //
404 // Signals and signal masks
405 //
406 SigSet sigMask(SigSet set, int how /* = SIG_SETMASK */)
407 {
408 sigset_t old;
409 checkError(::sigprocmask(how, &set.value(), &old));
410 return old;
411 }
412
413
414 //
415 // Make or use a directory, open-style.
416 //
417 // Flags are to be interpreted like open(2) flags; particularly
418 // O_CREAT make the directory if not present
419 // O_EXCL fail if the directory is present
420 // Other open(2) flags are currently ignored.
421 //
422 // Yes, it's a function.
423 //
424 void makedir(const char *path, int flags, mode_t mode)
425 {
426 struct stat st;
427 if (!stat(path, &st)) {
428 if (flags & O_EXCL)
429 UnixError::throwMe(EEXIST);
430 if (!S_ISDIR(st.st_mode))
431 UnixError::throwMe(ENOTDIR);
432 secdebug("makedir", "%s exists", path);
433 return;
434 }
435
436 // stat failed
437 if (errno != ENOENT || !(flags & O_CREAT))
438 UnixError::throwMe();
439
440 // ENOENT and creation enabled
441 if (::mkdir(path, mode)) {
442 if (errno == EEXIST && !(flags & O_EXCL))
443 return; // fine (race condition, resolved)
444 UnixError::throwMe();
445 }
446 secdebug("makedir", "%s created", path);
447 }
448
449
450 //
451 // Open, read/write, close a (small) file on disk
452 //
453 int ffprintf(const char *path, int flags, mode_t mode, const char *format, ...)
454 {
455 FileDesc fd(path, flags, mode);
456 FILE *f = fd.fdopen("w");
457 va_list args;
458 va_start(args, format);
459 int rc = vfprintf(f, format, args);
460 va_end(args);
461 if (fclose(f))
462 UnixError::throwMe();
463 return rc;
464 }
465
466 int ffscanf(const char *path, const char *format, ...)
467 {
468 if (FILE *f = fopen(path, "r")) {
469 va_list args;
470 va_start(args, format);
471 int rc = vfscanf(f, format, args);
472 va_end(args);
473 if (!fclose(f))
474 return rc;
475 }
476 UnixError::throwMe();
477 }
478
479
480 } // end namespace IPPlusPlus
481 } // end namespace Security