Logo Search packages:      
Sourcecode: ksh version File versions  Download package

csopen.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1990-2007 AT&T Intellectual Property          *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Research
 *
 * access/open/create path of the form
 *
 *    /dev/<type>/<host>/<service>[/<qualifier>][/<perm>][/#option][/#/path]
 *
 * type:    {fdp,tcp,udp}
 * host:    open={<hostname>}, create={share,local}
 * service: {.../lib/cs/<type>/<service>/server,.../bin/<service>}
 * qualifier:     optional service qualifier
 * perm:    {user[=euid],group[=egid],other(default)}
 */

#include "cslib.h"

#include <error.h>
#include <proc.h>
#include <hashkey.h>

#define REMOTE_ARGC     10
#define REMOTE_FLAGC    12

/*
 * create the state->mount <type>/<host>/<service>/<perm> subdirs
 * state->mount modified, possibly destroyed on error
 */

static int
mkmount(register Cs_t* state, int mode, int uid, int gid, char* endserv, char* endhost, char* endtype)
{
      *(state->control - 1) = 0;
      if (eaccess(state->mount, R_OK|W_OK|X_OK))
      {
            if (errno != ENOENT)
                  return -1;
            if (!endserv && !(endserv = strrchr(state->mount, '/')))
                  return -1;
            *endserv = 0;
            if (eaccess(state->mount, X_OK))
            {
                  if (!endhost && !(endhost = strrchr(state->mount, '/')))
                        return -1;
                  *endhost = 0;
                  if (eaccess(state->mount, X_OK))
                  {
                        if (!endtype && !(endtype = strrchr(state->mount, '/')))
                              return -1;
                        *endtype = 0;
                        if (eaccess(state->mount, X_OK) && (mkdir(state->mount, S_IRWXU|S_IRWXG|S_IRWXO) || chmod(state->mount, S_IRWXU|S_IRWXG|S_IRWXO)))
                              return -1;
                        *endtype = '/';
                        if (mkdir(state->mount, S_IRWXU|S_IRWXG|S_IRWXO) || chmod(state->mount, S_IRWXU|S_IRWXG|S_IRWXO))
                              return -1;
                  }
                  *endhost = '/';
                  if (mkdir(state->mount, S_IRWXU|S_IRWXG|S_IRWXO) || chmod(state->mount, S_IRWXU|S_IRWXG|S_IRWXO))
                        return -1;
            }
            *endserv = '/';
            if (mkdir(state->mount, mode))
                  return -1;
            if (mode != (S_IRWXU|S_IRWXG|S_IRWXO) && (uid >= 0 || gid >= 0 && (mode |= S_ISGID)) && (chown(state->mount, uid, gid) || chmod(state->mount, mode)))
            {
                  rmdir(state->mount);
                  return -1;
            }
      }
      *(state->control - 1) = '/';
      return 0;
}

/*
 * generate CS_REMOTE_CONTROL argv
 */

static void
remote(register Cs_t* state, const char* host, const char* user, const char* path, int agent, register char** av, register char* fv)
{
      register char*    t;

      *av++ = CS_REMOTE_SHELL;
      *av++ = (char*)host;
      if (user && *user)
      {
            *av++ = "-l";
            *av++ = (char*)user;
      }
      *av++ = CS_REMOTE_PROFILE;
      *av++ = ";";
      *av++ = CS_REMOTE_CONTROL;
      *av++ = fv;
      *av++ = (char*)path;
      *av = 0;
      *fv++ = '-';
      if (error_info.trace < 0)
            fv += sfsprintf(fv, 4, "%c%d", CS_REMOTE_DEBUG, -error_info.trace);
      *fv++ = CS_REMOTE_OPEN;
      t = fv;
      if (agent)
            *fv++ = CS_REMOTE_OPEN_AGENT;
      if (state->flags & CS_ADDR_LOCAL)
            *fv++ = CS_REMOTE_OPEN_LOCAL;
      if (state->flags & CS_ADDR_NOW)
            *fv++ = CS_REMOTE_OPEN_NOW;
      if (state->flags & CS_ADDR_SHARE)
            *fv++ = CS_REMOTE_OPEN_SHARE;
      if (state->flags & CS_ADDR_TEST)
            *fv++ = CS_REMOTE_OPEN_TEST;
      if (state->flags & CS_ADDR_TRUST)
            *fv++ = CS_REMOTE_OPEN_TRUST;
      if (fv == t)
            *fv++ = CS_REMOTE_OPEN_READ;
      *fv = 0;
}

/*
 * initiate service named by path on state->host w/state->flags
 * service is modified in place
 * 0 returned on success
 */

static int
initiate(register Cs_t* state, char* user, char* path, char* service, char* name)
{
      register char*    s;
      char*       on;
      char*       local;
      Sfio_t*           sp;
      Sfio_t*           np;
      int         n;
      char*       av[REMOTE_ARGC];
      char        buf[PATH_MAX / 4];

      local = csname(state, 0);
      s = service + strlen(service);
      *s++ = '/';
      if (!(state->flags & CS_ADDR_SHARE))
      {
            sfsprintf(buf, sizeof(buf), "%s\n", state->host);
            if (!(sp = tokline(buf, SF_STRING, NiL)))
                  return -1;
      }
      else if (state->flags & CS_ADDR_LOCAL)
      {
            sfsprintf(buf, sizeof(buf), "%s\n", CS_HOST_LOCAL);
            if (!(sp = tokline(buf, SF_STRING, NiL)))
                  return -1;
      }
      else
      {
            strcpy(s, CS_SVC_HOSTS);
            if (!(sp = tokline(service, SF_READ, NiL)))
            {
                  if (streq(state->host, CS_HOST_SHARE))
                        sfsprintf(buf, sizeof(buf), "%s\n%s\n", CS_HOST_SHARE, CS_HOST_LOCAL);
                  else
                        sfsprintf(buf, sizeof(buf), "%s\n%s\n%s\n", state->host, CS_HOST_SHARE, CS_HOST_LOCAL);
                  if (!(sp = tokline(buf, SF_STRING, NiL)))
                        return -1;
            }
      }
      sfsprintf(s, PATH_MAX - (s - service) - 1, "%s%s", name, CS_SVC_SUFFIX);
      while (s = sfgetr(sp, '\n', 1))
            if (tokscan(s, NiL, " %s ", &on) == 1)
            {
                  if (streq(on, CS_HOST_LOCAL) || streq(on, local))
                  {
                        sfclose(sp);
                        av[0] = service;
                        av[1] = path;
                        av[2] = 0;
                        return procclose(procopen(av[0], av, NiL, NiL, PROC_PRIVELEGED|PROC_ZOMBIE)) < 0 ? -1 : 0;
                  }
                  else if (!streq(on, CS_HOST_SHARE))
                  {
                        Proc_t*           proc;
                        time_t            otime;
                        struct stat st;
                        char        fv[REMOTE_FLAGC];
                        long        ov[3];

                        remote(state, on, user, path, 0, av, fv);
                        otime = lstat(state->mount, &st) ? 0 : st.st_ctime;
                        n = open("/dev/null", O_RDWR);
                        ov[0] = PROC_FD_DUP(n, 0, 0);
                        ov[1] = PROC_FD_DUP(n, 1, PROC_FD_PARENT|PROC_FD_CHILD);
                        ov[2] = 0;
                        if (proc = procopen(av[0], av, NiL, ov, PROC_PRIVELEGED|PROC_ZOMBIE))
                        {
                              n = 1;
                              for (;;)
                              {
                                    if (!lstat(state->mount, &st) && st.st_ctime != otime)
                                    {
                                          if (!procclose(proc))
                                          {
                                                sfclose(sp);
                                                return 0;
                                          }
                                          break;
                                    }

                                    /*
                                     * sleep() and MNT_TMP
                                     * hack around remote
                                     * fs cache delays
                                     */

                                    if (n >= CS_REMOTE_DELAY)
                                    {
                                          procclose(proc);
                                          break;
                                    }
                                    if (n == 1)
                                    {
                                          *state->control = CS_MNT_TMP;
                                          if (remove(state->mount))
                                          {
                                                close(open(state->mount, O_WRONLY|O_CREAT|O_TRUNC, 0));
                                                remove(state->mount);
                                          }
                                          *state->control = CS_MNT_STREAM;
                                    }
                                    sleep(n);
                                    n <<= 1;
                              }
                        }
                  }
                  else if (!sfstacked(sp) && (np = csinfo(state, on, NiL)))
                        sfstack(sp, np);
            }
      sfclose(sp);
      return -1;
}

/*
 * recursive csopen(state,path,CS_OPEN_TEST)
 * previous side effects preserved
 */

static int
reopen(register Cs_t* state, char* path)
{
      int         ret;
      Cs_t        tmp;

      static int  level;

      if (level >= 8)
            return -1;
      tmp = *state;
      level++;
      ret = csopen(&tmp, path, CS_OPEN_TEST);
      level--;
      state->addr = tmp.addr;
      state->port = tmp.port;
      return ret;
}

/*
 * use remote agent to initiate and authenticate service on path
 */

static int
agent(register Cs_t* state, const char* host, const char* user, const char* path)
{
      register int      n;
      register int      m;
      register char*    s;
      Proc_t*           proc;
      int         fd = -1;
      char*       av[REMOTE_ARGC];
      char        fv[REMOTE_FLAGC];
      char        buf[PATH_MAX / 4];
      char        num[64];
      char        tmp[64];

      remote(state, host, user, path, 1, av, fv);
      if (!(proc = procopen(av[0], av, NiL, NiL, PROC_READ|PROC_WRITE|PROC_PRIVELEGED)))
            goto sorry;
      if ((m = csread(state, proc->rfd, buf, sizeof(buf), CS_LINE)) <= 1)
            goto sorry;
      buf[--m] = 0;
      if (buf[--m] == 'A')
            buf[--m] = 0;
      else
            m = 0;
      if ((fd = reopen(state, buf)) < 0)
            goto sorry;
      if (m)
      {
            if ((m = csread(state, proc->rfd, buf, sizeof(buf), CS_LINE)) <= 1)
                  goto sorry;
            if (cswrite(state, fd, buf, m) != m)
                  goto sorry;
            if ((n = csread(state, fd, num, sizeof(num), CS_LINE)) <= 0)
                  goto sorry;
            if (*num != '\n')
            {
                  buf[m - 1] = 0;
                  num[n - 1] = 0;
                  n = sfsprintf(tmp, sizeof(tmp), "%s %s\n", num, (s = strchr(buf, ' ')) ? (s + 1) : buf);
                  if (cswrite(state, proc->wfd, tmp, n) != n)
                        goto sorry;
                  if ((m = csread(state, proc->rfd, buf, sizeof(buf), CS_LINE)) <= 0)
                        goto sorry;
                  if (cswrite(state, fd, buf, m) != m)
                        goto sorry;
                  if (csread(state, fd, num, 1, CS_LINE) != 1)
                        goto sorry;
                  if (cswrite(state, proc->wfd, num, 1) != 1)
                        goto sorry;
            }
      }
      procclose(proc);
      return fd;
 sorry:
      if (fd >= 0)
            close(fd);
      if (proc)
            procclose(proc);
      errno = EACCES;
      return -1;
}

/*
 * csattach() helper
 */

static int
doattach(register Cs_t* state, const char* path, int op, int mode, char* user, char* opath, char* tmp, char* serv, char*b)
{
      register int      n;
      int         fd;
      char*       s;

#if CS_LIB_STREAM || CS_LIB_V10

      int         fds[2];
      struct stat st;

      if (op & CS_OPEN_CREATE)
      {
            n = errno;
            if (chmod(path, mode))
            {
                  remove(path);
                  if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0)
                  {
                        messagef((state->id, NiL, -1, "open: %s: %s: creat %o error", state->path, path, mode));
                        return -1;
                  }
                  close(fd);
                  chmod(path, mode);
            }
            errno = n;
            if (pipe(fds))
            {
                  messagef((state->id, NiL, -1, "open: %s: pipe error", state->path, path));
                  return -1;
            }

#if CS_LIB_V10

            if ((n = ioctl(fds[1], FIOPUSHLD, &conn_ld)) || fmount(3, fds[1], path, 0))

#else

            if ((n = ioctl(fds[1], I_PUSH, "connld")) || fattach(fds[1], path))

#endif

            {
                  messagef((state->id, NiL, -1, "open: %s: %s: %s error", state->path, path, n ? "connld" : "fattach"));
                  close(fds[0]);
                  close(fds[1]);
                  errno = ENXIO;
                  return -1;
            }
            close(fds[1]);
            fd = fds[0];
      }
      else
            for (;;)
            {
                  if ((fd = open(path, O_RDWR)) >= 0)
                  {
                        if (!fstat(fd, &st) && !S_ISREG(st.st_mode))
                              break;
                        close(fd);
                        remove(path);
                  }
                  else if ((op & CS_OPEN_TEST) || errno == EACCES)
                  {
                        messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path));
                        return -1;
                  }
                  if (initiate(state, user, opath, tmp, serv))
                  {
                        messagef((state->id, NiL, -1, "open: %s: %s: service initiate error", state->path, path));
                        return -1;
                  }
                  op = CS_OPEN_TEST;
            }

#else

#if CS_LIB_SOCKET_UN

      int               pid;
      int               namlen;
      char              c;
      struct sockaddr_un      nam;

      messagef((state->id, NiL, -8, "AHA:%s:%d state.path=`%s' state.mount=`%s' path=`%s' opath=`%s' user=`%s' serv=`%s'", __FILE__, __LINE__, state->path, state->mount, path, opath, user, serv));
      nam.sun_family = AF_UNIX;
      strcpy(nam.sun_path, path);
      namlen = sizeof(nam.sun_family) + strlen(path) + 1;
      for (n = 0;; n++)
      {
            if (n >= 10)
            {
                  errno = ENXIO;
            badcon:
                  close(fd);
                  return -1;
            }
            if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
            {
                  messagef((state->id, NiL, -1, "open: %s: %s: AF_UNIX socket error", state->path, path));
                  return -1;
            }
            if (!connect(fd, (struct sockaddr*)&nam, namlen))
            {
                  if (op & CS_OPEN_CREATE)
                  {
                        errno = EEXIST;
                        goto badcon;
                  }
#if CS_LIB_SOCKET_RIGHTS
                  if (read(fd, &c, 1) == 1 && !cssend(state, fd, NiL, 0))
                        break;
#else
                  break;
#endif
            }
            else
            {
                  messagef((state->id, NiL, -1, "open: %s: %s: connect error", state->path, path));
                  if (errno == EACCES)
                        goto badcon;
                  else if (errno == EADDRNOTAVAIL || errno == ECONNREFUSED)
                  {
                        c = 0;
                        for (;;)
                        {
                              *b = CS_MNT_PROCESS;
                              pid = pathgetlink(path, state->temp, sizeof(state->temp));
                              *b = CS_MNT_STREAM;
                              if (pid > 0 || ++c >= 5)
                                    break;
                              sleep(1);
                        }
                        if (pid > 0 && (s = strrchr(state->temp, '/')) && (pid = strtol(s + 1, NiL, 0)) > 0)
                        {
                              if (!kill(pid, 0) || errno != ESRCH)
                              {
                                    if (op & CS_OPEN_CREATE)
                                    {
                                          errno = EEXIST;
                                          goto badcon;
                                    }
                                    close(fd);
                                    if (n)
                                          sleep(1);
                                    continue;
                              }
                              *b = CS_MNT_PROCESS;
                              remove(path);
                              *b = CS_MNT_STREAM;
                        }
                  }
            }
            close(fd);
            errno = ENOENT;
            if (op & CS_OPEN_CREATE)
            {
                  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
                  {
                        messagef((state->id, NiL, -1, "open: %s: %s: AF_UNIX socket error", state->path, path));
                        return -1;
                  }
                  if (!bind(fd, (struct sockaddr*)&nam, namlen))
                  {
                        chmod(path, mode);
                        if (listen(fd, 32))
                        {
                              messagef((state->id, NiL, -1, "open: %s: %s: listen error", state->path, path));
                              n = errno;
                              remove(path);
                              errno = n;
                              goto badcon;
                        }
                        break;
                  }
                  else
                        messagef((state->id, NiL, -1, "open: %s: %s: bind error", state->path, path));
                  if (errno != EADDRINUSE || n && remove(path) && errno != ENOENT)
                        goto badcon;
                  close(fd);
            }
            else if (op & CS_OPEN_TEST)
                  return -1;
            else if (!n && initiate(state, user, opath, tmp, serv))
            {
                  messagef((state->id, NiL, -1, "open: %s: %s: service initiate error", state->path, path));
                  return -1;
            }
            else
                  sleep(2);
      }

#else

      errno = (op & CS_OPEN_CREATE) ? ENXIO : ENOENT;
      messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, path));
      fd = -1;

#endif

#endif

#if CS_LIB_SOCKET_UN || CS_LIB_STREAM || CS_LIB_V10

      touch(path, (time_t)0, (time_t)0, 0);
      strcpy(state->path, path);

#endif

      return fd;
}

/*
 * return connect stream to fdp path
 * if (op & CS_) then path is created
 * and server connect stream is returned
 * otherwise client connect stream returned
 */

int
csattach(register Cs_t* state, const char* path, int op, int mode)
{
      return doattach(state, path, op, 0, 0, 0, 0, 0, 0);
}

int
_cs_attach(const char* path, int op, int mode)
{
      return csattach(&cs, path, op, mode);
}

/*
 * access/open/create service on path
 */

int
csopen(register Cs_t* state, const char* apath, int op)
{
      register char*    path = (char*)apath;
      register char*    b;
      register char*    s;
      register int      n;
      int         fd;
      char*       t;
      char*       u;
      char*       type;
      char*       endtype;
      char*       host;
      char*       endhost;
      char*       serv;
      char*       endserv;
      char*       qual;
      char*       endqual;
      char*       opath;
      char*       user = 0;
      char*       group = 0;
      char*       trust = 0;
      char*       arg = 0;
      int         nfd = -1;
      int         uid = -1;
      int         gid = -1;
      int         sid = -1;
      int         auth = 1;
      int         mode;
      unsigned long     addr;
      unsigned long     port = 0;
      struct stat st;
      gid_t       groups[NGROUPS_MAX + 1];
      char        buf[PATH_MAX];
      char        tmp[PATH_MAX];

      if (!path)
      {
            errno = EFAULT;
            return -1;
      }
      csprotect(&cs);
      if (op < 0)
            op = CS_OPEN_TEST;
      messagef((state->id, NiL, -8, "open(%s,%o) call", path, op));

      /*
       * blast out the parts
       */

      opath = path;
      if (pathgetlink(path, buf, sizeof(buf)) <= 0)
      {
            if (strlen(path) >= sizeof(buf))
                  return -1;
            strcpy(buf, path);
      }
      else if ((state->flags & CS_ADDR_LOCAL) && (s = strrchr(buf, '/')))
      {
            /*
             * dynamic ip assignment can change the addr
             * underfoot in some implementations so we
             * double check the local ip here
             */

            strcpy(tmp, buf);
            if (tokscan(tmp, NiL, "/dev/%s/%s/%s", &type, NiL, &serv) == 3)
                  sfsprintf(buf, sizeof(buf), "/dev/%s/%s/%s", type, csntoa(state, 0), serv);
      }
      path = buf;
      pathcanon(path, 0);
      errno = ENOENT;
      strcpy(state->path, path);
      b = path;
      if ((*b++ != '/') || !(s = strchr(b, '/')))
            return -1;
      *s++ = 0;
      if (!streq(b, "dev"))
            return -1;
      if (b = strchr(s, '/'))
            *b++ = 0;
      if (streq(s, "fdp"))
      {
#if !( CS_LIB_SOCKET_UN || CS_LIB_STREAM || CS_LIB_V10 )
            if (access(CS_PROC_FD_TST, F_OK))
            {
                  errno = ENODEV;
                  messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, s));
                  return -1;
            }
#endif
      }
      else if (!streq(s, "tcp") && !streq(s, "udp"))
      {
            messagef((state->id, NiL, -1, "open: %s: %s: invalid type", state->path, s));
            return -1;
      }
#if !( CS_LIB_SOCKET || CS_LIB_STREAM || CS_LIB_V10 )
      else
      {
            errno = ENODEV;
            messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, s));
            return -1;
      }
#endif
      type = s;
      qual = state->qual;
      if (!b)
            host = serv = 0;
      else
      {
            host = b;
            if (!(s = strchr(b, '/')))
                  serv = 0;
            else
            {
                  *s++ = 0;
                  serv = s;

                  /*
                   * grab the next fd to preserve open semantics
                   */

                  for (n = 0; n < 10; n++)
                        if ((nfd = dup(n)) >= 0)
                              break;

                  /*
                   * get qual, perm and arg
                   */

                  mode = S_IRWXU|S_IRWXG|S_IRWXO;
                  if (b = strchr(s, '/'))
                  {
                        *b++ = 0;
                        do
                        {
                              if (*b == '#')
                              {
                                    arg = b + 1;
                                    break;
                              }
                              if (u = strchr(b, '/'))
                                    *u++ = 0;
                              if (s = strchr(b, '='))
                                    *s++ = 0;
                              for (n = 0, t = b; *t; n = HASHKEYPART(n, *t++));
                              switch (n)
                              {
                              case HASHKEY5('g','r','o','u','p'):
                                    group = s ? s : "";
                                    break;
                              case HASHKEY5('l','o','c','a','l'):
                                    op |= CS_OPEN_LOCAL;
                                    break;
                              case HASHKEY3('n','o','w'):
                                    op |= CS_OPEN_NOW;
                                    break;
                              case HASHKEY5('o','t','h','e','r'):
                                    auth = 0;
                                    break;
                              case HASHKEY6('r','e','m','o','t','e'):
                                    op |= CS_OPEN_REMOTE;
                                    break;
                              case HASHKEY5('s','h','a','r','e'):
                                    op |= CS_OPEN_SHARE;
                                    break;
                              case HASHKEY5('s','l','a','v','e'):
                                    op |= CS_OPEN_SLAVE;
                                    break;
                              case HASHKEY4('t','e','s','t'):
                                    op |= CS_OPEN_TEST;
                                    break;
                              case HASHKEY5('t','r','u','s','t'):
                                    op |= CS_OPEN_TRUST;
                                    trust = s;
                                    break;
                              case HASHKEY4('u','s','e','r'):
                                    user = s ? s : "";
                                    break;
                              default:
                                    qual += sfsprintf(qual, sizeof(state->qual) - (qual - state->qual) - 1, "%s%s", qual == state->qual ? "" : "-", b);
                                    if (s)
                                          *(s - 1) = '=';
                                    break;
                              }
                        } while (b = u);
                  }
            }
      }
      if (*type != 't')
            auth = 0;
      strncpy(state->type, type, sizeof(state->type) - 1);
      qual = (qual == state->qual) ? (char*)0 : state->qual;
      messagef((state->id, NiL, -8, "open: type=%s host=%s serv=%s qual=%s", type, host, serv, qual));
      if (host)
      {
            /*
             * validate host
             */

            if (!(state->addr = addr = csaddr(state, host)))
            {
                  if (serv && !(op & CS_OPEN_CREATE) && *type == 't' && (port = csport(state, type, serv)) >= CS_PORT_MIN && port <= CS_PORT_MAX)
                  {
                        /*
                         * attempt proxy connection
                         */

                        if (nfd >= 0)
                        {
                              close(nfd);
                              nfd = -1;
                        }
                        if ((fd = state->proxy.addr ? csbind(state, type, state->proxy.addr, state->proxy.port, 0L) : reopen(state, csvar(state, CS_VAR_PROXY, 0))) >= 0)
                        {
                              state->proxy.addr = state->addr;
                              state->proxy.port = state->port;
                              n = sfsprintf(tmp, sizeof(tmp), "\n%s!%s!%d\n\n%s\n%s\n0\n-1\n-1\n", type, host, port, csname(state, 0), error_info.id ? error_info.id : state->id);
                              if (cswrite(state, fd, tmp, n) == n && (n = csread(state, fd, tmp, sizeof(tmp), CS_LINE)) >= 2)
                              {
                                    if (tmp[0] == '0' && tmp[1] == '\n')
                                          return fd;
                                    if (error_info.trace <= -4 && n > 2)
                                    {
                                          s = tmp;
                                          s[n - 1] = 0;
                                          while (*s && *s++ != '\n');
                                          messagef((state->id, NiL, -4, "%s error message `%s'", csvar(state, CS_VAR_PROXY, 0), s));
                                    }
                              }
                              close(fd);
                        }
                  }
#ifdef EADDRNOTAVAIL
                  errno = EADDRNOTAVAIL;
#else
                  errno = ENOENT;
#endif
                  goto bad;
            }
            if (op & CS_OPEN_LOCAL)
            {
                  state->flags |= CS_ADDR_LOCAL;
                  state->flags &= ~CS_ADDR_REMOTE;
            }
            if (op & CS_OPEN_NOW)
                  state->flags |= CS_ADDR_NOW;
            if ((op & (CS_OPEN_AGENT|CS_OPEN_REMOTE)) == CS_OPEN_REMOTE)
            {
                  state->flags |= CS_ADDR_REMOTE;
                  state->flags &= ~CS_ADDR_LOCAL;
            }
            if (op & CS_OPEN_SHARE)
                  state->flags |= CS_ADDR_SHARE;
            if (op & CS_OPEN_SLAVE)
                  state->flags |= CS_DAEMON_SLAVE;
            if (op & CS_OPEN_TEST)
                  state->flags |= CS_ADDR_TEST;
            if (op & CS_OPEN_TRUST)
                  state->flags |= CS_ADDR_TRUST;
            if ((state->flags & CS_ADDR_REMOTE) && (!serv || !strneq(serv, CS_SVC_INET, sizeof(CS_SVC_INET) - 1) && (strtol(serv, &t, 0), *t)))
                  return agent(state, state->host, state->user, state->path);
            if (s = user)
            {
                  n = geteuid();
                  if (*s)
                  {
                        if ((uid = struid(s)) < 0)
                        {
                              uid = strtol(s, &t, 0);
                              if (*t)
                              {
                                    errno = EACCES;
                                    goto bad;
                              }
                        }
                        if (n && uid != n)
                        {
                              errno = EACCES;
                              goto bad;
                        }
                  }
                  else
                        uid = n;
                  mode &= ~(S_IRWXG|S_IRWXO);
            }
            if (s = group)
            {
                  n = getegid();
                  if (*s)
                  {
                        if ((gid = strgid(s)) < 0)
                        {
                              gid = strtol(s, &t, 0);
                              if (*t)
                              {
                                    errno = EACCES;
                                    goto bad;
                              }
                        }
                        if (geteuid() && gid != n)
                        {
                              for (n = getgroups(elementsof(groups), groups); n >= 0; n--)
                                    if (gid == groups[n])
                                          break;
                              if (n < 0)
                              {
                                    errno = EACCES;
                                    goto bad;
                              }
                        }
                  }
                  else
                        gid = n;
                  mode &= ~S_IRWXO;
            }
            if (s = trust)
            {
                  if (!*s)
                        sid = geteuid();
                  else if ((sid = struid(s)) < 0)
                  {
                        sid = strtol(s, &t, 0);
                        if (*t)
                        {
                              errno = EACCES;
                              goto bad;
                        }
                  }
            }
            if (state->flags & CS_ADDR_SHARE)
                  host = CS_HOST_SHARE;
            else
            {
                  host = state->host;
                  if (!(state->flags & CS_ADDR_LOCAL))
                  {
                        if (*type == 'f')
                        {
                              errno = ENODEV;
                              goto bad;
                        }
                        if (op & CS_OPEN_CREATE)
                        {
                              errno = EROFS;
                              goto bad;
                        }
                  }
                  if (serv && !qual && *type != 'f' && (port = csport(state, type, serv)) != CS_PORT_INVALID)
                  {
                        if (op & CS_OPEN_CREATE)
                              addr = 0;
                        else if (port == CS_PORT_RESERVED || port == CS_PORT_NORMAL)
                              goto bad;
                        if (nfd >= 0)
                        {
                              close(nfd);
                              nfd = -1;
                        }
                        state->control = 0;
                        if ((fd = csbind(state, type, addr, port, 0L)) >= 0)
                        {
                              if (mode != (S_IRWXU|S_IRWXG|S_IRWXO) && csauth(state, fd, NiL, NiL))
                              {
                                    close(fd);
                                    return -1;
                              }
                              return fd;
                        }
                  }
            }
      }

      /*
       * get the mount dir prefix
       */

      if (opath == (b = path = state->mount))
      {
#ifdef ELOOP
            errno = ELOOP;
#else
            errno = EINVAL;
#endif
            goto bad;
      }
      if (*type == 'f')
      {
            if (host && !(state->flags & CS_ADDR_LOCAL))
            {
                  errno = ENODEV;
                  goto bad;
            }
            b += sfsprintf(b, sizeof(state->mount) - (b - path), "%s", csvar(state, CS_VAR_LOCAL, 0));
            if ((op & CS_OPEN_CREATE) && eaccess(path, X_OK) && (mkdir(path, S_IRWXU|S_IRWXG|S_IRWXO) || chmod(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)))
                  goto bad;
      }
      else
      {
            if (op & CS_OPEN_TRUST)
            {
                  if (!pathaccess(b, csvar(state, CS_VAR_TRUST, 1), csvar(state, CS_VAR_SHARE, 1), NiL, PATH_EXECUTE))
                        goto bad;
            }
            else if (!pathpath(b, csvar(state, CS_VAR_SHARE, 0), "", PATH_EXECUTE))
                  goto bad;
            b += strlen(b);
      }

      /*
       * add the type
       */

      b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", type);
      if (!host)
      {
            *(state->control = b + 1) = 0;
            if (nfd >= 0)
                  close(nfd);
            if ((fd = open(path, O_RDONLY)) < 0)
            {
                  mkmount(state, S_IRWXU|S_IRWXG|S_IRWXO, -1, -1, NiL, NiL, NiL);
                  fd = open(path, O_RDONLY);
            }
            if (fd < 0)
                  messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path));
            return fd;
      }
      endtype = b;

      /*
       * add the host
       */

      if (strlen(host) <= CS_MNT_MAX)
            b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", host);
      else
      {
            s = csntoa(state, addr);
            if (strlen(s) <= CS_MNT_MAX)
                  b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", s);
            else
            {
                  unsigned char*    a = (unsigned char*)&addr;

                  b += sfsprintf(b, sizeof(state->mount) - (b - path), "/0x%X.%X.%X.%X", a[0], a[1], a[2], a[3]);
            }
      }
      messagef((state->id, NiL, -8, "AHA:%s:%d host=`%s' path=`%s'", __FILE__, __LINE__, host, path));
      if (!serv)
      {
            *(state->control = b + 1) = 0;
            if (nfd >= 0)
                  close(nfd);
            if ((fd = open(path, O_RDONLY)) < 0)
                  messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path));
            return fd;
      }
      endhost = b;

      /*
       * add the service
       */

      sfsprintf(b, sizeof(state->mount) - (b - path), "%s/%s/%s/%s%s", CS_SVC_DIR, type, serv, serv, CS_SVC_SUFFIX);
      if (!pathpath(tmp, b, "", PATH_ABSOLUTE|PATH_EXECUTE) || stat(tmp, &st))
            op |= CS_OPEN_TEST;
      else
      {
            *strrchr(tmp, '/') = 0;
            if (!(op & CS_OPEN_TRUST))
                  sid = st.st_uid;
            if (!st.st_size)
                  op |= CS_OPEN_TEST;
      }
      b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", serv);
      endserv = b;

      /*
       * add the qualifier and perm
       */

      if (sid >= 0)
            b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%d-", sid);
      else
            b += sfsprintf(b, sizeof(state->mount) - (b - path), "/-");
      if (uid >= 0)
            b += sfsprintf(b, sizeof(state->mount) - (b - path), "%d-", uid);
      else if (gid >= 0)
            b += sfsprintf(b, sizeof(state->mount) - (b - path), "-%d", gid);
      else
            b += sfsprintf(b, sizeof(state->mount) - (b - path), "-");
#if limit_qualifier_length
      endqual = endserv + CS_MNT_MAX + 1;
#else
      endqual = state->mount + sizeof(state->mount) - 1;
#endif
      if (qual)
      {
            if (b < endqual)
                  *b++ = '-';
            while (b < endqual && *qual)
                  *b++ = *qual++;
      }
      if (*type == 't' && !auth)
      {
            if (b >= endqual)
                  b--;
            *b++ = CS_MNT_OTHER;
      }

      /*
       * add in the connect stream control
       */

      *b++ = '/';
      *b = CS_MNT_STREAM;
      strcpy(b + 1, CS_MNT_TAIL);
      messagef((state->id, NiL, -8, "AHA:%s:%d %s", __FILE__, __LINE__, state->mount));
      state->control = b;

      /*
       * create the mount subdirs if necessary
       */

      if ((op & CS_OPEN_CREATE) && mkmount(state, mode, uid, gid, endserv, endhost, endtype))
            goto bad;
      mode &= S_IRWXU|S_IRWXG|S_IRWXO;
      if (nfd >= 0)
      {
            close(nfd);
            nfd = -1;
      }
      if (op & CS_OPEN_MOUNT)
      {
            messagef((state->id, NiL, -1, "open(%s,%o) = %d, mount = %s", state->path, op, state->mount));
            return 0;
      }
      if (*type == 'f')
      {
            /*
             * {fdp}
             */

            if ((fd = doattach(state, path, op, mode, user, opath, tmp, serv, b)) < 0)
                  return -1;
      }
      else
      {
            /*
             * {tcp,udp}
             */

            messagef((state->id, NiL, -8, "AHA:%s:%d %s", __FILE__, __LINE__, state->mount));
            if ((fd = reopen(state, path)) < 0)
            {
                  /*
                   * check for old single char cs mount
                   */

                  *(state->control + 1) = 0;
                  if ((fd = reopen(state, path)) < 0)
                        messagef((state->id, NiL, -1, "open: %s: %s: reopen error", state->path, path));
                  *(state->control + 1) = CS_MNT_TAIL[0];
            }
            if (op & CS_OPEN_CREATE)
            {
                  if (fd >= 0)
                  {
                        close(fd);
                        errno = EEXIST;
                        return -1;
                  }
                  if (errno != ENOENT && errno != ENOTDIR)
                        return -1;
                  sigcritical(1);
                  *state->control = CS_MNT_LOCK;
                  if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0)) < 0)
                  {
                        if (stat(path, &st))
                        {
                              messagef((state->id, NiL, -1, "open: %s: %s: creat error", state->path, path));
                              goto unblock;
                        }
                        if ((CSTIME() - (unsigned long)st.st_ctime) < 2 * 60)
                        {
                              errno = EEXIST;
                              messagef((state->id, NiL, -1, "open: %s: %s: another server won the race", state->path, path));
                              goto unblock;
                        }
                        if (remove(path))
                        {
                              messagef((state->id, NiL, -1, "open: %s: %s: remove error", state->path, path));
                              goto unblock;
                        }
                        if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0)) < 0)
                        {
                              messagef((state->id, NiL, -1, "open: %s: %s: creat error", state->path, path));
                              goto unblock;
                        }
                  }
                  close(fd);
                  if (!port && (n = strtol(serv, &t, 0)) && t > serv && !*t)
                        port = n;
                  else if (geteuid())
                        port = CS_NORMAL;
                  else
                        port = CS_RESERVED;
                  if ((fd = csbind(state, type, 0L, port, 0L)) >= 0)
                  {
                        *state->control = CS_MNT_STREAM;
                        remove(path);
                        if (pathsetlink(cspath(state, fd, 0), path))
                        {
                              messagef((state->id, NiL, -1, "open: %s: %s: link error", cspath(state, fd, 0), path));
                              close(fd);
                              fd = -1;
                        }
                  }
            unblock:
                  *state->control = CS_MNT_LOCK;
                  remove(path);
                  sigcritical(0);
                  *state->control = CS_MNT_STREAM;
                  if (fd < 0)
                        return -1;
            }
            else if (fd < 0 && ((op & CS_OPEN_TEST) || initiate(state, user, opath, tmp, serv) || (fd = reopen(state, path)) < 0))
            {
                  messagef((state->id, NiL, -1, "open: %s: %s: reopen/initiate error", state->path, path));
                  return -1;
            }
            else if (!(op & CS_OPEN_AGENT))
            {
                  *state->control = CS_MNT_AUTH;
                  n = csauth(state, fd, path, arg);
                  *state->control = CS_MNT_STREAM;
                  if (n)
                  {
                        close(fd);
                        messagef((state->id, NiL, -1, "open: %s: %s: authentication error", state->path, path));
                        return -1;
                  }
            }
      }

      /*
       * fd is open at this point
       * make sure its not a bogus mount
       */

      if (mode != (S_IRWXU|S_IRWXG|S_IRWXO))
      {
            *state->control = 0;
            n = stat(path, &st);
            *state->control = CS_MNT_STREAM;
            if (n)
            {
                  messagef((state->id, NiL, -1, "open: %s: %s: stat error", state->path, path));
                  close(fd);
                  return -1;
            }
            if (uid >= 0 && st.st_uid != uid || gid >= 0 && st.st_gid != gid)
            {
                  close(fd);
                  errno = EPERM;
                  messagef((state->id, NiL, -1, "open: %s: %s: uid/gid error", state->path, path));
                  return -1;
            }
      }
      return fd;
 bad:
      if (nfd >= 0)
            close(nfd);
      return -1;
}

int
_cs_open(const char* apath, int op)
{
      return csopen(&cs, apath, op);
}

Generated by  Doxygen 1.6.0   Back to index