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 35namespace Security { 36namespace UnixPlusPlus { 37 38using 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// 45void 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 58void 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// 71size_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 91size_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// 108size_t FileDesc::read(void *addr, size_t length, size_t position) 109{ 110 return checkError(::pread(mFd, addr, length, position)); 111} 112 113size_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// 122size_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 134size_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 149void 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 164size_t FileDesc::seek(size_t position, int whence) 165{ 166 return (size_t)checkError(::lseek(mFd, position, whence)); 167} 168 169size_t FileDesc::position() const 170{ 171 return (size_t)checkError(::lseek(mFd, 0, SEEK_CUR)); 172} 173 174 175// 176// Mmap support 177// 178void *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// 192int 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// 203void 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// 215FileDesc FileDesc::dup() const 216{ 217 return FileDesc(checkError(::dup(mFd)), atEnd()); 218} 219 220FileDesc FileDesc::dup(int newFd) const 221{ 222 return FileDesc(checkError(::dup2(mFd, newFd)), atEnd()); 223} 224 225 226// 227// Advisory locking, fcntl style 228// 229void 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 236bool 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 253void 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// 266int 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// 278void 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 284ssize_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 297ssize_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 311void 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 324size_t FileDesc::listAttr(char *value, size_t length, int options /* = 0 */) 325{ 326 return checkError(::flistxattr(mFd, value, length, options)); 327} 328 329 330void 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 335std::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 345bool 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// 357// Stat support 358// 359void FileDesc::fstat(UnixStat &st) const 360{ 361 if (::fstat(mFd, &st)) 362 UnixError::throwMe(); 363} 364 365size_t FileDesc::fileSize() const 366{ 367 struct stat st; 368 fstat(st); 369 return (size_t)st.st_size; 370} 371 372bool FileDesc::isA(int mode) const 373{ 374 struct stat st; 375 fstat(st); 376 return (st.st_mode & S_IFMT) == mode; 377} 378 379 380void FileDesc::chown(uid_t uid) 381{ 382 checkError(::fchown(mFd, uid, gid_t(-1))); 383} 384 385void FileDesc::chown(uid_t uid, gid_t gid) 386{ 387 checkError(::fchown(mFd, uid, gid)); 388} 389 390void FileDesc::chgrp(gid_t gid) 391{ 392 checkError(::fchown(mFd, uid_t(-1), gid)); 393} 394 395void FileDesc::chmod(mode_t mode) 396{ 397 checkError(::fchmod(mFd, mode)); 398} 399 400void FileDesc::chflags(u_int flags) 401{ 402 checkError(::fchflags(mFd, flags)); 403} 404 405 406FILE *FileDesc::fdopen(const char *form) 407{ 408 //@@@ pick default value for 'form' based on chracteristics of mFd 409 return ::fdopen(mFd, form); 410} 411 412 413// 414// Signals and signal masks 415// 416SigSet sigMask(SigSet set, int how /* = SIG_SETMASK */) 417{ 418 sigset_t old; 419 checkError(::sigprocmask(how, &set.value(), &old)); 420 return old; 421} 422 423 424// 425// Make or use a directory, open-style. 426// 427// Flags are to be interpreted like open(2) flags; particularly 428// O_CREAT make the directory if not present 429// O_EXCL fail if the directory is present 430// Other open(2) flags are currently ignored. 431// 432// Yes, it's a function. 433// 434void makedir(const char *path, int flags, mode_t mode) 435{ 436 struct stat st; 437 if (!stat(path, &st)) { 438 if (flags & O_EXCL) 439 UnixError::throwMe(EEXIST); 440 if (!S_ISDIR(st.st_mode)) 441 UnixError::throwMe(ENOTDIR); 442 secdebug("makedir", "%s exists", path); 443 return; 444 } 445 446 // stat failed 447 if (errno != ENOENT || !(flags & O_CREAT)) 448 UnixError::throwMe(); 449 450 // ENOENT and creation enabled 451 if (::mkdir(path, mode)) { 452 if (errno == EEXIST && !(flags & O_EXCL)) 453 return; // fine (race condition, resolved) 454 UnixError::throwMe(); 455 } 456 secdebug("makedir", "%s created", path); 457} 458 459 460// 461// Open, read/write, close a (small) file on disk 462// 463int ffprintf(const char *path, int flags, mode_t mode, const char *format, ...) 464{ 465 FileDesc fd(path, flags, mode); 466 FILE *f = fd.fdopen("w"); 467 va_list args; 468 va_start(args, format); 469 int rc = vfprintf(f, format, args); 470 va_end(args); 471 if (fclose(f)) 472 UnixError::throwMe(); 473 return rc; 474} 475 476int ffscanf(const char *path, const char *format, ...) 477{ 478 if (FILE *f = fopen(path, "r")) { 479 va_list args; 480 va_start(args, format); 481 int rc = vfscanf(f, format, args); 482 va_end(args); 483 if (!fclose(f)) 484 return rc; 485 } 486 UnixError::throwMe(); 487} 488 489 490} // end namespace IPPlusPlus 491} // end namespace Security 492