//
// aegis - project change supervisor
// Copyright (C) 1993-1995, 1997-1999, 2001-2008, 2012 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
//
// Most of the functions in this file are only used when
// the CONF_NO_seteuid symbol is defined in "[arch]/common/config.h".
//
// The various system calls wgich must be perfomed as a specific user
// are given to a "proxy" process to perform, where this proxy process
// has the appropriate real (and effective) user id and group id.
// These processes terminate when they see end-of-file on their command
// streams, which only happens when the parent process exits.
//
// The overhead of communicating with the proxy process will
// naturally result in a performance hit for systems without
// a seteuid system call.
//
// Note that some systems have a seteuid system call
// which is broken. These systems will also need to use this glue.
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//
// Turn off the function defines in aegis/glue.h so that we can
// define the glue functions without the macros getting in the way.
//
#define aegis_glue_disable
#include
#include
#include
#include
#include
#include // for lock_release_child
#include
#include
#include
#define GUARD1 0x416E6479
#define GUARD2 0x4C777279
enum
{
command_access,
command_catfile,
command_chmod,
command_chown,
command_close,
command_copyfile,
command_creat,
command_fcntl,
command_file_compare,
command_file_fingerprint,
command_getcwd,
command_link,
command_lstat,
command_lutime,
command_mkdir,
command_open,
command_pathconf,
command_read,
command_readlink,
command_read_whole_dir,
command_rename,
command_rmdir,
command_rmdir_bg,
command_rmdir_tree,
command_stat,
command_symlink,
command_unlink,
command_utime,
command_write
};
struct proxy_ty
{
int hash;
int uid;
int gid;
int umask;
int pid;
FILE *command;
FILE *reply;
proxy_ty *next;
};
#define proxy_hash(u, g, m) (((u) * 16 + (g) * 4 + (m)) & 0x7FFF)
//
// This table will be sparsely filled.
// The maximum number of entries expected is 4.
// The size MUST be prime.
//
static proxy_ty *proxy_table[17];
#ifdef DEBUG
static const char *
command_name(int n)
{
static char buf[12];
switch (n)
{
case EOF: return "quit";
case command_access: return "access";
case command_catfile: return "catfile";
case command_chmod: return "chmod";
case command_chown: return "chown";
case command_close: return "close";
case command_copyfile: return "copyfile";
case command_creat: return "creat";
case command_fcntl: return "fcntl";
case command_file_compare: return "file_compare";
case command_file_fingerprint: return "file_fingerprint";
case command_getcwd: return "getcwd";
case command_link: return "link";
case command_lstat: return "lstat";
case command_lutime: return "lutime";
case command_mkdir: return "mkdir";
case command_open: return "open";
case command_pathconf: return "pathconf";
case command_read: return "read";
case command_readlink: return "readlink";
case command_read_whole_dir: return "read_whole_dir";
case command_rename: return "rename";
case command_rmdir: return "rmdir";
case command_rmdir_bg: return "rmdir_bg";
case command_rmdir_tree: return "rmdir_tree";
case command_symlink: return "symlink";
case command_stat: return "stat";
case command_unlink: return "unlink";
case command_utime: return "utime";
case command_write: return "write";
}
snprintf(buf, sizeof(buf), "%d", n);
return buf;
}
#endif // DEBUG
static void
put_int(FILE *fp, int n)
{
trace(("put_int(%d)\n{\n", n));
unsigned char *ptr = (unsigned char *)&n;
for (size_t j = 0; j < sizeof(int); ++j)
fputc(ptr[j], fp);
if (ferror(fp))
nfatal("writing pipe");
trace(("}\n"));
}
static int
get_int(FILE *fp)
{
int result;
size_t j;
unsigned char *ptr;
trace(("get_int()\n{\n"));
ptr = (unsigned char *)&result;
for (j = 0; j < sizeof(int); ++j)
{
int c = fgetc(fp);
if (c == EOF)
{
if (ferror(fp))
nfatal("reading pipe");
fatal_raw("reading pipe: proxy protocol error (%d)", getpid());
}
ptr[j] = c;
}
trace(("return %d;\n", result));
trace(("}\n"));
return result;
}
static void
put_long(FILE *fp, long n)
{
size_t j;
unsigned char *ptr;
trace(("put_long(%ld)\n{\n", n));
ptr = (unsigned char *)&n;
for (j = 0; j < sizeof(long); ++j)
fputc(ptr[j], fp);
if (ferror(fp))
nfatal("writing pipe");
trace(("}\n"));
}
static long
get_long(FILE *fp)
{
long result;
size_t j;
unsigned char *ptr;
trace(("get_long()\n{\n"));
ptr = (unsigned char *)&result;
for (j = 0; j < sizeof(long); ++j)
{
int c = fgetc(fp);
if (c == EOF)
{
if (ferror(fp))
nfatal("reading pipe");
fatal_raw("reading pipe: proxy protocol error (%d)", getpid());
}
ptr[j] = c;
}
trace(("return %ld;\n", result));
trace(("}\n"));
return result;
}
static void
put_binary(FILE *fp, const void *ptr, size_t len)
{
trace(("put_binary(%ld)\n{\n", (long)len));
fwrite(ptr, 1, len, fp);
if (ferror(fp))
nfatal("writing pipe");
trace(("}\n"));
}
static void
get_binary(FILE *fp, void *ptr, size_t len)
{
long n;
trace(("get_binary(%ld)\n{\n", (long)len));
n = fread(ptr, 1, len, fp);
if ((size_t)n != len)
{
if (ferror(fp))
nfatal("reading pipe");
fatal_raw("reading pipe: proxy protocol error (%d)", getpid());
}
trace(("}\n"));
}
static void
put_string(FILE *fp, const char *s)
{
trace(("put_string(\"%s\")\n{\n", s));
for (;;)
{
fputc(*s, fp);
if (ferror(fp))
nfatal("writing pipe");
if (!*s)
break;
++s;
}
trace(("}\n"));
}
static void *
get_string(FILE *fp)
{
static char *result;
static size_t result_max;
size_t pos;
trace(("get_string()\n{\n"));
if (!result)
{
result_max = (1L << 10 ) - 32;
result = (char *)mem_alloc(result_max);
}
pos = 0;
for (;;)
{
int c = fgetc(fp);
if (c == EOF)
{
if (ferror(fp))
nfatal("reading pipe");
fatal_raw("reading pipe: proxy protocol error (%d)", getpid());
}
if (pos >= result_max)
{
result_max = result_max * 2 + 16;
char *new_result = new char [result_max];
memcpy(new_result, result, result_max);
delete [] result;
result = new_result;
}
result[pos] = c;
if (!c)
break;
++pos;
}
trace(("return \"%s\";\n", result));
trace(("}\n"));
return result;
}
static void
proxy(int rd_fd, int wr_fd)
{
trace(("proxy(%d, %d)\n{\n", rd_fd, wr_fd));
errno = 0;
FILE *command = fdopen(rd_fd, "r");
if (!command)
{
if (!errno)
errno = ENOMEM;
exit(errno);
}
errno = 0;
FILE *reply = fdopen(wr_fd, "w");
if (!reply)
{
if (!errno)
errno = ENOMEM;
exit(errno);
}
for (;;)
{
int c = fgetc(command);
trace(("command: %s\n", command_name(c)));
trace(("uid = %d;\n", getuid()));
trace(("gid = %d;\n", getgid()));
switch (c)
{
case EOF:
if (ferror(command))
exit(errno);
exit(0);
default:
fatal_raw("proxy: unknown %d command (bug)", c);
case command_access:
{
char *path = (char *)get_string(command);
int mode = get_int(command);
int result = 0;
if (access(path, mode))
result = errno;
put_int(reply, result);
}
break;
case command_catfile:
{
char *path = (char *)get_string(command);
int result = 0;
if (catfile(path))
result = errno;
put_int(reply, result);
}
break;
case command_chmod:
{
char *path = (char *)get_string(command);
int mode = get_int(command);
int result = 0;
if (chmod(path, mode))
result = errno;
put_int(reply, result);
}
break;
case command_chown:
{
char *path = (char *)get_string(command);
int uid = get_int(command);
int gid = get_int(command);
int result = 0;
if (chown(path, uid, gid))
result = errno;
put_int(reply, result);
}
break;
case command_close:
{
int fd = get_int(command);
int result = 0;
if (close(fd))
result = errno;
put_int(reply, result);
}
break;
case command_copyfile:
{
char *path = (char *)get_string(command);
char *path1 = mem_copy_string(path);
path = (char *)get_string(command);
int result = copyfile(path1, path);
if (result)
result = errno;
mem_free(path1);
put_int(reply, result);
}
break;
case command_creat:
{
char *path = (char *)get_string(command);
int mode = get_int(command);
int result = creat(path, mode);
put_int(reply, result);
if (result < 0)
put_int(reply, errno);
}
break;
case command_getcwd:
{
int path_max = get_int(command);
char *path = (char *)mem_alloc(path_max);
if (!getcwd(path, path_max))
put_int(reply, errno);
else
{
put_int(reply, 0);
put_string(reply, path);
}
mem_free(path);
}
break;
case command_fcntl:
{
int fd = get_int(command);
int mode = get_int(command);
struct flock theFlock;
get_binary(command, &theFlock, sizeof(theFlock));
int result = fcntl(fd, mode, &theFlock);
if (result)
result = errno;
put_int(reply, result);
put_binary(reply, &theFlock, sizeof(theFlock));
}
break;
case command_file_compare:
{
char *path = (char *)get_string(command);
char *path1 = mem_copy_string(path);
path = (char *)get_string(command);
int result = file_compare(path1, path);
if (result < 0)
result = -errno;
mem_free(path1);
put_int(reply, result);
}
break;
case command_file_fingerprint:
{
char *path = (char *)get_string(command);
int path1_max = get_int(command);
char *path1 = (char *)mem_alloc(path1_max + 1);
int result = file_fingerprint(path, path1, path1_max);
put_int(reply, result);
if (result < 0)
put_int(reply, errno);
else
put_binary(reply, path1, result);
mem_free(path1);
}
break;
case command_link:
{
char *path = (char *)get_string(command);
char *path1 = mem_copy_string(path);
char *path2 = (char *)get_string(command);
int result = link(path1, path2);
if (result)
result = errno;
mem_free(path1);
put_int(reply, result);
}
break;
case command_lstat:
{
char *path = (char *)get_string(command);
struct stat st;
#ifdef S_IFLNK
int result = lstat(path, &st);
#else
int result = stat(path, &st);
#endif
if (result)
put_int(reply, errno);
else
{
put_int(reply, 0);
put_binary(reply, &st, sizeof(st));
}
}
break;
case command_lutime:
{
char *path = (char *)get_string(command);
struct utimbuf utb;
get_binary(command, &utb, sizeof(utb));
#ifdef HAVE_LUTIME
int result = utime(path, &utb);
if (result)
result = errno;
#else
(void)path;
int result = EINVAL;
#endif
put_int(reply, result);
}
break;
case command_mkdir:
{
char *path = (char *)get_string(command);
int mode = get_int(command);
int result = 0;
if (mkdir(path, mode))
result = errno;
put_int(reply, result);
}
break;
case command_open:
{
char *path = (char *)get_string(command);
int mode = get_int(command);
int perm = get_int(command);
int result = open(path, mode, perm);
put_int(reply, result);
if (result < 0)
put_int(reply, errno);
}
break;
case command_pathconf:
{
char *path = (char *)get_string(command);
int mode = get_int(command);
#ifndef _PC_NAME_MAX
(void)path;
(void)mode;
put_long(reply, -1L);
put_int(reply, EINVAL);
#else
errno = EINVAL;
long nbytes = pathconf(path, mode);
put_long(reply, nbytes);
if (nbytes < 0)
put_int(reply, errno);
#endif
}
break;
case command_rename:
{
char *path = (char *)get_string(command);
char *path1 = mem_copy_string(path);
path = (char *)get_string(command);
int result = rename(path1, path);
if (result)
result = errno;
mem_free(path1);
put_int(reply, result);
}
break;
case command_read:
{
int fd = get_int(command);
long nbytes = get_long(command);
char *buf = (char *)mem_alloc(nbytes);
long nbytes2 = read(fd, buf, nbytes);
put_long(reply, nbytes2);
if (nbytes2 > 0)
put_binary(reply, buf, nbytes2);
else if (nbytes2 < 0)
put_int(reply, errno);
mem_free(buf);
}
break;
case command_readlink:
{
char *path = (char *)get_string(command);
int path1_max = get_int(command);
char *path1 = (char *)mem_alloc(path1_max + 1);
#ifdef S_IFLNK
int result = readlink(path, path1, path1_max);
put_int(reply, result);
if (result < 0)
put_int(reply, errno);
else
put_binary(reply, path1, result);
#else
put_int(reply, -1);
put_int(reply, EINVAL);
#endif
mem_free(path1);
}
break;
case command_read_whole_dir:
{
char *path = (char *)get_string(command);
long nbytes = 0;
char *buf = 0;
int result = read_whole_dir(path, &buf, &nbytes);
if (result < 0)
put_int(reply, errno);
else
{
put_int(reply, 0);
put_long(reply, nbytes);
put_binary(reply, buf, nbytes);
}
// do not free *buf, or *buf[*]
}
break;
case command_rmdir:
{
char *path = (char *)get_string(command);
int result = 0;
if (rmdir(path))
result = errno;
put_int(reply, result);
}
break;
case command_rmdir_bg:
{
char *path = (char *)get_string(command);
int result = 0;
if (rmdir_bg(path))
result = errno;
put_int(reply, result);
}
break;
case command_rmdir_tree:
{
char *path = (char *)get_string(command);
int result = 0;
if (rmdir_tree(path))
result = errno;
put_int(reply, result);
}
break;
case command_stat:
{
char *path = (char *)get_string(command);
struct stat st;
int result = stat(path, &st);
if (result)
put_int(reply, errno);
else
{
put_int(reply, 0);
put_binary(reply, &st, sizeof(st));
}
}
break;
case command_symlink:
{
char *path = (char *)get_string(command);
char *path1 = mem_copy_string(path);
char *path2 = (char *)get_string(command);
#ifdef S_IFLNK
int result = symlink(path1, path2);
if (result)
result = errno;
#else
int result = EINVAL;
#endif
mem_free(path1);
put_int(reply, result);
}
break;
case command_unlink:
{
char *path = (char *)get_string(command);
int result = unlink(path);
if (result)
result = errno;
put_int(reply, result);
}
break;
case command_utime:
{
char *path = (char *)get_string(command);
struct utimbuf utb;
get_binary(command, &utb, sizeof(utb));
int result = utime(path, &utb);
if (result)
result = errno;
put_int(reply, result);
}
break;
case command_write:
{
int fd = get_int(command);
long nbytes = get_long(command);
char *buf = (char *)mem_alloc(nbytes);
get_binary(command, buf, nbytes);
long nbytes2 = write(fd, buf, nbytes);
put_long(reply, nbytes2);
if (nbytes2 < 0)
put_int(reply, errno);
mem_free(buf);
}
break;
}
fflush(reply);
if (ferror(reply))
exit(errno);
}
trace(("}\n"));
}
static void
get_pipe(int *rd, int *wr)
{
int fd[2];
trace(("get_pipe()\n{\n"));
if (pipe(fd))
nfatal("pipe");
*rd = fd[0];
*wr = fd[1];
trace(("read %d\n", fd[0]));
trace(("write %d\n", fd[1]));
trace(("}\n"));
}
static void
proxy_close(void)
{
proxy_ty *p;
size_t j;
trace(("proxy_close()\n{\n"));
trace(("pid = %d;\n", getpid()));
for (j = 0; j < SIZEOF(proxy_table); ++j)
{
for (;;)
{
p = proxy_table[j];
if (!p)
break;
trace(("p->pid %d; uid %d; gid %d\n",
p->pid, p->uid, p->gid));
proxy_table[j] = p->next;
fclose(p->command);
fclose(p->reply);
mem_free((char *)p);
}
}
trace(("}\n"));
}
static void
proxy_spawn(proxy_ty *pp)
{
int command_read_fd;
int command_write_fd;
int reply_read_fd;
int reply_write_fd;
int pid;
static int quitregd;
trace(("proxy_spawn()\n{\n"));
if (!quitregd)
{
quitregd = 1;
signal(SIGPIPE, SIG_IGN);
trace(("master pid %d;\n", getpid()));
}
get_pipe(&command_read_fd, &command_write_fd);
get_pipe(&reply_read_fd, &reply_write_fd);
switch (pid = fork())
{
case -1:
nfatal("fork");
case 0:
//
// close the ends of the pipes the proxy will not be using
//
close(command_write_fd);
close(reply_read_fd);
os_interrupt_ignore();
//
// the proxy now assumes the appropriate ID
//
if (setgid(pp->gid))
exit(errno);
if (setuid(pp->uid))
exit(errno);
umask(pp->umask);
//
// close all of the master ends of all the other proxys
// otherwise they will keep each other alive
// after the master dies
//
trace_indent_reset();
proxy_close();
//
// normally the proxys are silent,
// returning all errors to the master.
// Should one of the error functions be called,
// make sure the proxy does not perform the undo.
//
undo_cancel();
//
// do whatever is asked
//
proxy(command_read_fd, reply_write_fd);
exit(0);
default:
//
// close the ends of the pipes the master will not be using
//
close(command_read_fd);
close(reply_write_fd);
//
// remember who the child is
// (even though we don't have a use for it at the moment)
//
pp->pid = pid;
trace(("child pid %d\n", getpid()));
//
// open a buffered stream for commands
//
errno = 0;
pp->command = fdopen(command_write_fd, "w");
if (!pp->command)
{
if (!errno)
errno = ENOMEM;
nfatal("fdopen");
}
//
// open a buffered stream for replies
//
errno = 0;
pp->reply = fdopen(reply_read_fd, "r");
if (!pp->reply)
{
if (!errno)
errno = ENOMEM;
nfatal("fdopen");
}
break;
}
trace(("}\n"));
}
static proxy_ty *
proxy_find(void)
{
int uid;
int gid;
int um;
int hash;
int pix;
proxy_ty *pp;
//
// search for an existing proxy
//
trace(("proxy_find()\n{\n"));
os_become_must_be_active();
os_become_query(&uid, &gid, &um);
hash = proxy_hash(uid, gid, um);
pix = hash % SIZEOF(proxy_table);
for (pp = proxy_table[pix]; pp; pp = pp->next)
{
if (pp->hash != hash)
continue;
if (pp->uid != uid || pp->gid != gid || pp->umask != um)
continue;
goto done;
}
//
// no such proxy, so create a new one
//
trace(("uid = %d; gid = %d; umask = 0%o;\n", uid, gid, um));
pp = (proxy_ty *)mem_alloc(sizeof(proxy_ty));
pp->hash = hash;
pp->uid = uid;
pp->gid = gid;
pp->umask = um;
pp->pid = -1;
pp->command = 0;
pp->reply = 0;
pp->next = 0;
proxy_spawn(pp);
//
// glue into the table AFTER the spawn,
// so the child doesn't close the wrong things.
//
pp->next = proxy_table[pix];
proxy_table[pix] = pp;
//
// here for all exits
//
done:
trace(("return %p;\n", pp));
trace(("}\n"));
return pp;
}
static void
end_of_command(proxy_ty *pp)
{
trace(("end_of_command()\n{\n"));
if (fflush(pp->command))
nfatal("write pipe");
trace(("}\n"));
}
int
glue_stat(const char *path, struct stat *st)
{
proxy_ty *pp;
int result;
trace(("glue_stat()\n{\n"));
pp = proxy_find();
fputc(command_stat, pp->command);
put_string(pp->command, path);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
else
get_binary(pp->reply, st, sizeof(*st));
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_lstat(const char *path, struct stat *st)
{
proxy_ty *pp;
int result;
trace(("glue_lstat()\n{\n"));
pp = proxy_find();
fputc(command_lstat, pp->command);
put_string(pp->command, path);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
else
get_binary(pp->reply, st, sizeof(*st));
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_mkdir(const char *path, int mode)
{
proxy_ty *pp;
int result;
trace(("glue_mkdir()\n{\n"));
pp = proxy_find();
fputc(command_mkdir, pp->command);
put_string(pp->command, path);
put_int(pp->command, mode);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_chown(const char *path, int uid, int gid)
{
proxy_ty *pp;
int result;
trace(("glue_chown()\n{\n"));
pp = proxy_find();
fputc(command_chown, pp->command);
put_string(pp->command, path);
put_int(pp->command, uid);
put_int(pp->command, gid);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_catfile(const char *path)
{
proxy_ty *pp;
int result;
trace(("glue_catfile()\n{\n"));
pp = proxy_find();
fputc(command_catfile, pp->command);
put_string(pp->command, path);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_chmod(const char *path, int mode)
{
proxy_ty *pp;
int result;
trace(("glue_chmod()\n{\n"));
pp = proxy_find();
fputc(command_chmod, pp->command);
put_string(pp->command, path);
put_int(pp->command, mode);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_rmdir(const char *path)
{
proxy_ty *pp;
int result;
trace(("glue_rmdir()\n{\n"));
pp = proxy_find();
fputc(command_rmdir, pp->command);
put_string(pp->command, path);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_rmdir_bg(const char *path)
{
proxy_ty *pp;
int result;
trace(("glue_rmdir_bg()\n{\n"));
pp = proxy_find();
fputc(command_rmdir_bg, pp->command);
put_string(pp->command, path);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_rename(const char *p1, const char *p2)
{
proxy_ty *pp;
int result;
trace(("glue_rename()\n{\n"));
pp = proxy_find();
fputc(command_rename, pp->command);
put_string(pp->command, p1);
put_string(pp->command, p2);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_symlink(const char *name1, const char *name2)
{
proxy_ty *pp;
int result;
trace(("glue_symlink()\n{\n"));
pp = proxy_find();
fputc(command_symlink, pp->command);
put_string(pp->command, name1);
put_string(pp->command, name2);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_unlink(const char *path)
{
proxy_ty *pp;
int result;
trace(("glue_unlink()\n{\n"));
pp = proxy_find();
fputc(command_unlink, pp->command);
put_string(pp->command, path);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_link(const char *p1, const char *p2)
{
proxy_ty *pp;
int result;
trace(("glue_link()\n{\n"));
pp = proxy_find();
fputc(command_link, pp->command);
put_string(pp->command, p1);
put_string(pp->command, p2);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_access(const char *path, int mode)
{
proxy_ty *pp;
int result;
trace(("glue_access()\n{\n"));
pp = proxy_find();
fputc(command_access, pp->command);
put_string(pp->command, path);
put_int(pp->command, mode);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
char *
glue_getcwd(char *buf, int buf_len)
{
//
// don't bother with the case where buf
// is the NULL pointer, aegis never uses it.
//
trace(("glue_getcwd()\n{\n"));
assert(buf);
proxy_ty *pp = proxy_find();
fputc(command_getcwd, pp->command);
put_int(pp->command, buf_len);
end_of_command(pp);
int result = get_int(pp->reply);
char *s = 0;
if (result)
{
trace(("return NULL; /* errno = %d */\n", result));
errno = result;
}
else
{
s = (char *)get_string(pp->reply);
strendcpy(buf, s, buf + buf_len);
s = buf;
trace(("return \"%s\";\n", s));
}
trace(("}\n"));
return s;
}
int
glue_readlink(const char *path, char *buf, int buf_len)
{
trace(("glue_readlink()\n{\n"));
proxy_ty *pp = proxy_find();
fputc(command_readlink, pp->command);
put_string(pp->command, path);
put_int(pp->command, buf_len);
end_of_command(pp);
int result = get_int(pp->reply);
if (result < 0)
{
errno = get_int(pp->reply);
result = -1;
}
else
{
get_binary(pp->reply, buf, result);
trace(("buf = \"%.*s\";\n", result, buf));
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_utime(const char *path, struct utimbuf *values)
{
proxy_ty *pp;
int result;
trace(("glue_utime()\n{\n"));
pp = proxy_find();
fputc(command_utime, pp->command);
put_string(pp->command, path);
put_binary(pp->command, values, sizeof(*values));
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_lutime(const char *path, struct utimbuf *values)
{
#ifdef HAVE_LUTIME
proxy_ty *pp;
int result;
trace(("glue_utime()\n{\n"));
pp = proxy_find();
fputc(command_lutime, pp->command);
put_string(pp->command, path);
put_binary(pp->command, values, sizeof(*values));
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
#else
(void)path;
(void)values;
errno = EINVAL;
return -1;
#endif
}
int
glue_copyfile(const char *p1, const char *p2)
{
proxy_ty *pp;
int result;
trace(("glue_copyfile()\n{\n"));
pp = proxy_find();
fputc(command_copyfile, pp->command);
put_string(pp->command, p1);
put_string(pp->command, p2);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
struct glue_file_ty
{
long guard1;
char *path;
int fmode;
char *buffer_end;
char *buffer_pos;
int fd;
int errno_sequester;
int pushback;
proxy_ty *pp;
char buffer[1 << 11]; // fits within knl pipe buf
long guard2;
};
FILE *
glue_fopen(const char *path, const char *mode)
{
glue_file_ty *gfp;
proxy_ty *pp;
int fmode;
int fd;
trace(("glue_fopen()\n{\n"));
if (!strcmp(mode, "r"))
fmode = O_RDONLY;
else if (!strcmp(mode, "w"))
fmode = O_WRONLY | O_CREAT | O_TRUNC;
else
{
errno = EINVAL;
gfp = 0;
goto done;
}
pp = proxy_find();
fputc(command_open, pp->command);
put_string(pp->command, path);
put_int(pp->command, fmode);
put_int(pp->command, 0666);
end_of_command(pp);
fd = get_int(pp->reply);
if (fd < 0)
{
errno = get_int(pp->reply);
gfp = 0;
goto done;
}
//
// build our file structure
//
gfp = (glue_file_ty *)mem_alloc(sizeof(glue_file_ty));
gfp->pp = pp;
gfp->path = mem_copy_string(path);
gfp->fmode = fmode;
gfp->fd = fd;
gfp->guard1 = GUARD1;
gfp->guard2 = GUARD2;
if (gfp->fmode == O_RDONLY)
{
gfp->buffer_end = 0;
gfp->buffer_pos = 0;
}
else
{
gfp->buffer_pos = gfp->buffer;
gfp->buffer_end = gfp->buffer + sizeof(gfp->buffer);
}
gfp->errno_sequester = 0;
gfp->pushback = EOF;
//
// NOTE: this is veeerrry nasty.
// The return value is not really a valid FILE,
// but is only useful to the glue file functions.
//
done:
trace(("return %p; /* errno = %d */\n", gfp, errno));
trace(("}\n"));
return (FILE *)gfp;
}
int
glue_fclose(FILE *fp)
{
proxy_ty *pp;
glue_file_ty *gfp;
int result;
int result2;
//
// Leave the standard file streams alone.
// There is a chance these will be seen here.
//
if (fp == stdout || fp == stdin || fp == stderr)
return fclose(fp);
//
// flush the buffers
//
trace(("glue_fclose()\n{\n"));
gfp = (glue_file_ty *)fp;
result = (gfp->fmode != O_RDONLY && glue_fflush(fp)) ? errno : 0;
//
// locate the appropriate proxy
//
assert(gfp->guard1 == GUARD1);
assert(gfp->guard2 == GUARD2);
pp = gfp->pp;
//
// tell the proxy to close
//
fputc(command_close, pp->command);
put_int(pp->command, gfp->fd);
end_of_command(pp);
result2 = get_int(pp->reply);
if (!result)
result = result2;
//
// Fclose always closes the file,
// even when the implicit write fails.
// Always dispose of our data.
//
mem_free(gfp->path);
mem_free((char *)gfp);
//
// set errno and get out of here
//
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_fgetc(FILE *fp)
{
proxy_ty *pp;
glue_file_ty *gfp;
int result;
long nbytes;
//
// Leave the standard file streams alone.
// There is a chance these will be seen here.
//
if (fp == stdout || fp == stdin || fp == stderr)
return fgetc(fp);
//
// locate the appropriate proxy
//
gfp = (glue_file_ty *)fp;
assert(gfp->guard1 == GUARD1);
assert(gfp->guard2 == GUARD2);
pp = gfp->pp;
//
// complain if we are in an error state,
// or they asked for something stupid
//
if (gfp->errno_sequester)
{
errno = gfp->errno_sequester;
result = EOF;
goto done;
}
if (gfp->fmode != O_RDONLY)
{
gfp->errno_sequester = EINVAL;
errno = EINVAL;
result = EOF;
goto done;
}
//
// use pushback if there is anything in it
//
if (gfp->pushback != EOF)
{
result = gfp->pushback;
gfp->pushback = EOF;
goto done;
}
//
// use the buffer if there is anything in it
//
if (gfp->buffer_pos && gfp->buffer_pos < gfp->buffer_end)
{
result = (unsigned char)*gfp->buffer_pos++;
goto done;
}
gfp->buffer_pos = 0;
gfp->buffer_end = 0;
//
// tell the proxy to read another buffer-full
//
pp = gfp->pp;
fputc(command_read, pp->command);
put_int(pp->command, gfp->fd);
put_long(pp->command, (long)sizeof(gfp->buffer));
end_of_command(pp);
nbytes = get_long(pp->reply);
if (nbytes < 0)
{
gfp->errno_sequester = get_int(pp->reply);
errno = gfp->errno_sequester;
result = EOF;
goto done;
}
if (nbytes == 0)
{
errno = 0;
result = EOF;
goto done;
}
assert((size_t)nbytes <= sizeof(gfp->buffer));
get_binary(pp->reply, gfp->buffer, nbytes);
gfp->buffer_pos = gfp->buffer;
gfp->buffer_end = gfp->buffer + nbytes;
result = (unsigned char)*gfp->buffer_pos++;
//
// here for all exits
//
done:
return result;
}
ssize_t
glue_read(int fd, void *data, size_t len)
{
proxy_ty *pp;
long nbytes;
//
// Leave the standard file streams alone.
// There is a chance these will be seen here.
//
if (fd == fileno(stdout) || fd == fileno(stdin) || fd == fileno(stderr))
return read(fd, data, len);
//
// locate the appropriate proxy
//
pp = proxy_find();
//
// tell the proxy to read another buffer-full
//
fputc(command_read, pp->command);
put_int(pp->command, fd);
put_long(pp->command, len);
end_of_command(pp);
nbytes = get_long(pp->reply);
if (nbytes < 0)
{
errno = get_int(pp->reply);
return -1;
}
if (nbytes == 0)
{
errno = 0;
return 0;
}
assert((size_t)nbytes <= len);
get_binary(pp->reply, data, nbytes);
return nbytes;
}
int
glue_ungetc(int c, FILE *fp)
{
glue_file_ty *gfp;
int result;
//
// Leave the standard file streams alone.
// There is a chance these will be seen here.
//
if (fp == stdout || fp == stdin || fp == stderr)
return ungetc(c, fp);
//
// make a pointer to our data
//
trace(("glue_ungetc()\n{\n"));
result = EOF;
gfp = (glue_file_ty *)fp;
assert(gfp->guard1 == GUARD1);
assert(gfp->guard2 == GUARD2);
//
// make sure not too many
//
if (gfp->pushback != EOF || c == EOF)
{
gfp->errno_sequester = EINVAL;
errno = EINVAL;
gfp->pushback = EOF;
goto done;
}
//
// stash the returned char
//
gfp->pushback = (unsigned char)c;
result = (unsigned char)c;
//
// here for all exits
//
done:
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_fputc(int c, FILE *fp)
{
proxy_ty *pp;
glue_file_ty *gfp;
int result;
//
// Leave the standard file streams alone.
// There is a chance these will be seen here.
//
if (fp == stdout || fp == stdin || fp == stderr)
return fputc(c, fp);
//
// locate the appropriate proxy
//
result = EOF;
gfp = (glue_file_ty *)fp;
assert(gfp->guard1 == GUARD1);
assert(gfp->guard2 == GUARD2);
pp = gfp->pp;
//
// if the stream is in an error state,
// or we were asked to do something stupid,
// return the error
//
if (gfp->errno_sequester)
{
errno = gfp->errno_sequester;
goto done;
}
if (gfp->fmode == O_RDONLY)
{
gfp->errno_sequester = EINVAL;
errno = EINVAL;
goto done;
}
//
// if there is no room in the buffer,
// flush it to the proxy
//
assert(gfp->buffer_pos);
if (gfp->buffer_pos >= gfp->buffer_end)
{
long nbytes;
long nbytes2;
fputc(command_write, pp->command);
put_int(pp->command, gfp->fd);
nbytes = gfp->buffer_pos - gfp->buffer;
put_long(pp->command, nbytes);
put_binary(pp->command, gfp->buffer, nbytes);
end_of_command(pp);
nbytes2 = get_long(pp->reply);
if (nbytes2 < 0)
{
gfp->errno_sequester = get_int(pp->reply);
errno = gfp->errno_sequester;
goto done;
}
if (nbytes2 != nbytes)
{
gfp->errno_sequester = EIO;
errno = EIO;
goto done;
}
gfp->buffer_pos = gfp->buffer;
gfp->buffer_end = gfp->buffer + sizeof(gfp->buffer);
}
//
// stash the character
//
*gfp->buffer_pos++ = c;
result = (unsigned char)c;
//
// here for all exits
//
done:
return result;
}
int
glue_fwrite(char *buf, long len1, long len2, FILE *fp)
{
proxy_ty *pp;
glue_file_ty *gfp;
int result;
long len;
//
// Leave the standard file streams alone.
// There is a chance these will be seen here.
//
if (fp == stdout || fp == stdin || fp == stderr)
return fwrite(buf, len1, len2, fp);
//
// locate the appropriate proxy
//
result = EOF;
gfp = (glue_file_ty *)fp;
assert(gfp->guard1 == GUARD1);
assert(gfp->guard2 == GUARD2);
pp = gfp->pp;
//
// if the stream is in an error state,
// or we were asked to do something stupid,
// return the error
//
if (gfp->errno_sequester)
{
errno = gfp->errno_sequester;
goto done;
}
if (gfp->fmode == O_RDONLY)
{
gfp->errno_sequester = EINVAL;
errno = EINVAL;
goto done;
}
//
// push the bytes into the buffer
//
len = len1 * len2;
while (len > 0)
{
int c = (unsigned char)*buf++;
--len;
//
// if there is no room in the buffer,
// flush it to the proxy
//
assert(gfp->buffer_pos);
if (gfp->buffer_pos >= gfp->buffer_end)
{
long nbytes;
long nbytes2;
fputc(command_write, pp->command);
put_int(pp->command, gfp->fd);
nbytes = gfp->buffer_pos - gfp->buffer;
put_long(pp->command, nbytes);
put_binary(pp->command, gfp->buffer, nbytes);
end_of_command(pp);
nbytes2 = get_long(pp->reply);
if (nbytes2 < 0)
{
gfp->errno_sequester = get_int(pp->reply);
errno = gfp->errno_sequester;
goto done;
}
if (nbytes2 != nbytes)
{
gfp->errno_sequester = EIO;
errno = EIO;
goto done;
}
gfp->buffer_pos = gfp->buffer;
gfp->buffer_end = gfp->buffer + sizeof(gfp->buffer);
}
//
// stash the character
//
*gfp->buffer_pos++ = c;
}
result = len1;
//
// here for all exits
//
done:
return result;
}
int
glue_ferror(FILE *fp)
{
glue_file_ty *gfp;
//
// Leave the standard file streams alone.
// There is a chance these will be seen here.
//
if (fp == stdout || fp == stdin || fp == stderr)
return ferror(fp);
//
// locate the appropriate proxy
//
gfp = (glue_file_ty *)fp;
assert(gfp->guard1 == GUARD1);
assert(gfp->guard2 == GUARD2);
//
// set errno depending on
// the error for this stream.
//
if (gfp->errno_sequester)
{
errno = gfp->errno_sequester;
gfp->errno_sequester = 0;
return 1;
}
return 0;
}
int
glue_fflush(FILE *fp)
{
glue_file_ty *gfp;
proxy_ty *pp;
int result;
//
// Leave the standard file streams alone.
// There is a chance these will be seen here.
//
if (fp == stdout || fp == stdin || fp == stderr)
return fflush(fp);
//
// locate the appropriate proxy
//
trace(("glue_fflush()\n{\n"));
result = EOF;
gfp = (glue_file_ty *)fp;
assert(gfp->guard1 == GUARD1);
assert(gfp->guard2 == GUARD2);
pp = gfp->pp;
//
// if the stream is in an error state,
// don't do anything
//
if (gfp->errno_sequester)
{
errno = gfp->errno_sequester;
goto done;
}
if (gfp->fmode == O_RDONLY)
{
gfp->errno_sequester = EINVAL;
errno = EINVAL;
goto done;
}
//
// if there is anything in the buffer,
// send it to the proxy
//
if (gfp->buffer_pos && gfp->buffer_pos > gfp->buffer)
{
long nbytes;
long nbytes2;
fputc(command_write, pp->command);
put_int(pp->command, gfp->fd);
nbytes = gfp->buffer_pos - gfp->buffer;
put_long(pp->command, nbytes);
put_binary(pp->command, gfp->buffer, nbytes);
end_of_command(pp);
nbytes2 = get_long(pp->reply);
if (nbytes2 < 0)
{
gfp->errno_sequester = get_int(pp->reply);
errno = gfp->errno_sequester;
goto done;
}
if (nbytes2 != nbytes)
{
gfp->errno_sequester = EIO;
errno = EIO;
goto done;
}
gfp->buffer_pos = gfp->buffer;
gfp->buffer_end = gfp->buffer + sizeof(gfp->buffer);
}
result = 0;
//
// here for all exits
//
done:
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_open(const char *path, int mode, int perm)
{
proxy_ty *pp;
int result;
trace(("glue_open()\n{\n"));
pp = proxy_find();
fputc(command_open, pp->command);
put_string(pp->command, path);
put_int(pp->command, mode);
put_int(pp->command, perm);
end_of_command(pp);
result = get_int(pp->reply);
if (result < 0)
errno = get_int(pp->reply);
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_creat(const char *path, int mode)
{
proxy_ty *pp;
int result;
trace(("glue_creat()\n{\n"));
pp = proxy_find();
fputc(command_creat, pp->command);
put_string(pp->command, path);
put_int(pp->command, mode);
end_of_command(pp);
result = get_int(pp->reply);
if (result < 0)
errno = get_int(pp->reply);
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_close(int fd)
{
proxy_ty *pp;
int result;
trace(("glue_close()\n{\n"));
pp = proxy_find();
fputc(command_close, pp->command);
put_int(pp->command, fd);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_write(int fd, const void *buf, long len)
{
proxy_ty *pp;
int result;
trace(("glue_write()\n{\n"));
pp = proxy_find();
fputc(command_write, pp->command);
put_int(pp->command, fd);
put_long(pp->command, len);
put_binary(pp->command, buf, len);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_fcntl(int fd, int cmd, struct flock *data)
{
proxy_ty *pp;
int result;
trace(("glue_fcntl()\n{\n"));
assert(cmd == F_SETLKW || cmd == F_SETLK || cmd == F_UNLCK ||
cmd == F_GETLK);
pp = proxy_find();
fputc(command_fcntl, pp->command);
put_int(pp->command, fd);
put_int(pp->command, cmd);
put_binary(pp->command, data, sizeof(*data));
end_of_command(pp);
result = get_int(pp->reply);
get_binary(pp->reply, data, sizeof(*data));
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_file_compare(const char *p1, const char *p2)
{
proxy_ty *pp;
int result;
trace(("glue_file_compare()\n{\n"));
pp = proxy_find();
fputc(command_rename, pp->command);
put_string(pp->command, p1);
put_string(pp->command, p2);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = -result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_file_fingerprint(const char *path, char *buf, int buf_len)
{
proxy_ty *pp;
int result;
trace(("glue_file_fingerprint()\n{\n"));
pp = proxy_find();
fputc(command_file_fingerprint, pp->command);
put_string(pp->command, path);
put_int(pp->command, buf_len);
end_of_command(pp);
result = get_int(pp->reply);
if (result < 0)
{
errno = get_int(pp->reply);
result = -1;
}
else
{
get_binary(pp->reply, buf, result);
trace(("buf = \"%.*s\";\n", result, buf));
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_read_whole_dir(const char *path, char **data_p, long *data_len_p)
{
static char *data;
static long data_max;
long data_len;
proxy_ty *pp;
int result;
trace(("glue_read_whole_dir(path = \"%s\")\n{\n", path));
pp = proxy_find();
fputc(command_read_whole_dir, pp->command);
put_string(pp->command, path);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
else
{
data_len = get_long(pp->reply);
if (data_len > data_max)
{
for (;;)
{
data_max = data_max * 2 + 16;
if (data_len <= data_max)
break;
}
delete [] data;
data = new char [data_max];
}
get_binary(pp->reply, data, data_len);
*data_len_p = data_len;
*data_p = data;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
long
glue_pathconf(const char *path, int cmd)
{
proxy_ty *pp;
long result;
trace(("glue_pathconf()\n{\n"));
pp = proxy_find();
fputc(command_pathconf, pp->command);
put_string(pp->command, path);
put_int(pp->command, cmd);
end_of_command(pp);
result = get_long(pp->reply);
if (result < 0)
errno = get_int(pp->reply);
trace(("return %ld; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
int
glue_rmdir_tree(const char *path)
{
proxy_ty *pp;
int result;
trace(("glue_open()\n{\n"));
pp = proxy_find();
fputc(command_rmdir_tree, pp->command);
put_string(pp->command, path);
end_of_command(pp);
result = get_int(pp->reply);
if (result)
{
errno = result;
result = -1;
}
trace(("return %d; /* errno = %d */\n", result, errno));
trace(("}\n"));
return result;
}
// vim: set ts=8 sw=4 et :