#include "unix++.h"
#include <security_utilities/memutils.h>
#include <security_utilities/debugging.h>
#include <sys/xattr.h>
#include <cstdarg>
namespace Security {
namespace UnixPlusPlus {
using LowLevelMemoryUtilities::increment;
void FileDesc::open(const char *path, int flags, mode_t mode)
{
if ((mFd = ::open(path, flags, mode & ~S_IFMT)) == -1) {
if (errno == ENOENT && (mode & S_IFMT) == modeMissingOk) {
return;
} else {
UnixError::throwMe();
}
}
mAtEnd = false;
secdebug("unixio", "open(%s,0x%x,0x%x) = %d", path, flags, mode, mFd);
}
void FileDesc::close()
{
if (mFd >= 0) {
checkError(::close(mFd));
secdebug("unixio", "close(%d)", mFd);
mFd = invalidFd;
}
}
size_t FileDesc::read(void *addr, size_t length)
{
switch (ssize_t rc = ::read(mFd, addr, length)) {
case 0: if (length == 0) { secdebug("unixio", "%d zero read (ignored)", mFd);
return 0;
}
mAtEnd = true;
secdebug("unixio", "%d end of data", mFd);
return 0;
case -1: if (errno == EAGAIN)
return 0; UnixError::throwMe(); default: return rc;
}
}
size_t FileDesc::write(const void *addr, size_t length)
{
ssize_t rc = ::write(mFd, addr, length);
if (rc == -1) {
if (errno == EAGAIN)
return 0;
UnixError::throwMe();
}
return rc;
}
size_t FileDesc::read(void *addr, size_t length, size_t position)
{
return checkError(::pread(mFd, addr, length, position));
}
size_t FileDesc::write(const void *addr, size_t length, size_t position)
{
return checkError(::pwrite(mFd, addr, length, position));
}
size_t FileDesc::readAll(void *addr, size_t length)
{
size_t total = 0;
while (length > 0 && !atEnd()) {
size_t size = read(addr, length);
addr = increment(addr, size);
length -= size;
total += size;
}
return total;
}
size_t FileDesc::readAll(string &value)
{
string s;
while (!atEnd()) {
char buffer[256];
if (size_t size = read(buffer, sizeof(buffer))) {
s += string(buffer, size);
continue;
}
}
swap(value, s);
return value.length();
}
void FileDesc::writeAll(const void *addr, size_t length)
{
while (length > 0) {
size_t size = write(addr, length);
addr = increment(addr, size);
length -= size;
}
}
#warning Cast to size_t may loose precision, only a problem for large files.
size_t FileDesc::seek(size_t position, int whence)
{
return (size_t)checkError(::lseek(mFd, position, whence));
}
size_t FileDesc::position() const
{
return (size_t)checkError(::lseek(mFd, 0, SEEK_CUR));
}
void *FileDesc::mmap(int prot, size_t length, int flags, size_t offset, void *addr)
{
if (!(flags & (MAP_PRIVATE | MAP_SHARED))) flags |= MAP_PRIVATE;
void *result = ::mmap(addr, length ? length : fileSize(), prot, flags, mFd, offset);
if (result == MAP_FAILED)
UnixError::throwMe();
return result;
}
int FileDesc::fcntl(int cmd, void *arg) const
{
int rc = ::fcntl(mFd, cmd, arg);
secdebug("unixio", "%d fcntl(%d,%p) = %d", mFd, cmd, arg, rc);
return checkError(rc);
}
void FileDesc::setFlag(int flag, bool on) const
{
if (flag) { int oldFlags = flags();
flags(on ? (oldFlags | flag) : (oldFlags & ~flag));
}
}
FileDesc FileDesc::dup() const
{
return FileDesc(checkError(::dup(mFd)), atEnd());
}
FileDesc FileDesc::dup(int newFd) const
{
return FileDesc(checkError(::dup2(mFd, newFd)), atEnd());
}
void FileDesc::lock(int type, const Pos &pos)
{
LockArgs args(type, pos);
IFDEBUG(args.debug(fd(), "lock"));
checkError(fcntl(F_SETLKW, &args));
}
bool FileDesc::tryLock(int type, const Pos &pos)
{
LockArgs args(type, pos);
IFDEBUG(args.debug(fd(), "tryLock"));
try {
fcntl(F_SETLK, &args);
return true;
} catch (const UnixError &err) {
if (err.error == EAGAIN)
return false;
else
throw;
}
}
#if !defined(NDEBUG)
void FileDesc::LockArgs::debug(int fd, const char *what)
{
secdebug("fdlock", "%d %s %s:%ld(%ld)", fd, what,
(l_whence == SEEK_SET) ? "ABS" : (l_whence == SEEK_CUR) ? "REL" : "END",
long(l_start), long(l_len));
}
#endif //NDEBUG
int FileDesc::ioctl(int cmd, void *arg) const
{
int rc = ::ioctl(mFd, cmd, arg);
if (rc == -1)
UnixError::throwMe();
return rc;
}
void FileDesc::setAttr(const char *name, const void *value, size_t length,
u_int32_t position , int options )
{
checkError(::fsetxattr(mFd, name, value, length, position, options));
}
ssize_t FileDesc::getAttrLength(const char *name)
{
ssize_t rc = ::fgetxattr(mFd, name, NULL, 0, 0, 0);
if (rc == -1)
switch (errno) {
case ENOATTR:
return -1;
default:
UnixError::throwMe();
}
return rc;
}
ssize_t FileDesc::getAttr(const char *name, void *value, size_t length,
u_int32_t position , int options )
{
ssize_t rc = ::fgetxattr(mFd, name, value, length, position, options);
if (rc == -1)
switch (errno) {
case ENOATTR:
return -1;
default:
UnixError::throwMe();
}
return rc;
}
void FileDesc::removeAttr(const char *name, int options )
{
if (::fremovexattr(mFd, name, options))
switch (errno) {
case ENOATTR:
if (!(options & XATTR_REPLACE)) return; default:
UnixError::throwMe();
}
}
size_t FileDesc::listAttr(char *value, size_t length, int options )
{
return checkError(::flistxattr(mFd, value, length, options));
}
void FileDesc::setAttr(const std::string &name, const std::string &value, int options )
{
return setAttr(name, value.c_str(), value.size(), 0, options);
}
std::string FileDesc::getAttr(const std::string &name, int options )
{
char buffer[4096]; ssize_t length = getAttr(name, buffer, sizeof(buffer), 0, options);
if (length >= 0)
return string(buffer, length);
else
return string();
}
bool FileDesc::isPlainFile(const std::string &path)
{
UnixStat st1, st2;
this->fstat(st1);
if (::lstat(path.c_str(), &st2))
UnixError::throwMe();
return (st1.st_ino == st2.st_ino && S_ISREG(st2.st_mode));
}
void FileDesc::fstat(UnixStat &st) const
{
if (::fstat(mFd, &st))
UnixError::throwMe();
}
size_t FileDesc::fileSize() const
{
struct stat st;
fstat(st);
return (size_t)st.st_size;
}
bool FileDesc::isA(int mode) const
{
struct stat st;
fstat(st);
return (st.st_mode & S_IFMT) == mode;
}
void FileDesc::chown(uid_t uid)
{
checkError(::fchown(mFd, uid, gid_t(-1)));
}
void FileDesc::chown(uid_t uid, gid_t gid)
{
checkError(::fchown(mFd, uid, gid));
}
void FileDesc::chgrp(gid_t gid)
{
checkError(::fchown(mFd, uid_t(-1), gid));
}
void FileDesc::chmod(mode_t mode)
{
checkError(::fchmod(mFd, mode));
}
void FileDesc::chflags(u_int flags)
{
checkError(::fchflags(mFd, flags));
}
FILE *FileDesc::fdopen(const char *form)
{
return ::fdopen(mFd, form);
}
SigSet sigMask(SigSet set, int how )
{
sigset_t old;
checkError(::sigprocmask(how, &set.value(), &old));
return old;
}
void makedir(const char *path, int flags, mode_t mode)
{
struct stat st;
if (!stat(path, &st)) {
if (flags & O_EXCL)
UnixError::throwMe(EEXIST);
if (!S_ISDIR(st.st_mode))
UnixError::throwMe(ENOTDIR);
secdebug("makedir", "%s exists", path);
return;
}
if (errno != ENOENT || !(flags & O_CREAT))
UnixError::throwMe();
if (::mkdir(path, mode)) {
if (errno == EEXIST && !(flags & O_EXCL))
return; UnixError::throwMe();
}
secdebug("makedir", "%s created", path);
}
int ffprintf(const char *path, int flags, mode_t mode, const char *format, ...)
{
FileDesc fd(path, flags, mode);
FILE *f = fd.fdopen("w");
va_list args;
va_start(args, format);
int rc = vfprintf(f, format, args);
va_end(args);
if (fclose(f))
UnixError::throwMe();
return rc;
}
int ffscanf(const char *path, const char *format, ...)
{
if (FILE *f = fopen(path, "r")) {
va_list args;
va_start(args, format);
int rc = vfscanf(f, format, args);
va_end(args);
if (!fclose(f))
return rc;
}
UnixError::throwMe();
}
} }