Logo Search packages:      
Sourcecode: ksh version File versions

pathreal.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1989-2007 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                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>                  *
*                  David Korn <dgk@research.att.com>                   *
*                   Eduardo Krell <ekrell@adexus.cl>                   *
*                                                                      *
***********************************************************************/
#pragma prototyped

#include "3d.h"

#ifndef MAXSYMLINKS
#define MAXSYMLINKS     20
#endif

/*
 * if <sp> is a hard link to state.opaque in the same directory then the file
 * is opaque and the errno should be ENOENT
 */

static int
checkopaque(register char* path, struct stat* st)
{
      register char*    basesp;
      int         oerrno = errno;
      struct stat statb;
      char        savebuf[sizeof(state.opaque)];
      register int      r = 0;

      if (st->st_nlink <= 1)
            return 0;
      if (st->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))
            return 0;

      /*
       * change the basename to state.opaque
       */

      if (basesp = strrchr(path, '/'))
            basesp++;
      else
            basesp = path;
      memcpy(savebuf, basesp, sizeof(state.opaque));
      strcpy(basesp, state.opaque);
      if (LSTAT(path, &statb))
      {
            /*
             * for backward compatability
             */

            basesp[3] = 0;
            if (LSTAT(path, &statb))
                  goto not_opaque;
      }
      if (statb.st_ino == st->st_ino && statb.st_dev == st->st_dev)
      {
            errno = ENOENT;
            r = statb.st_ino;
      }
      else
            errno = oerrno;
 not_opaque:
      memcpy(basesp, savebuf, sizeof(state.opaque));
      return r;
}

/*
 * return real path name for path
 */

#if DEBUG

static char*      _pathreal(const char*, int, struct stat*);

char*
pathreal(const char* apath, register int type, struct stat* st)
{
      char* path = (char*)apath;

      initialize();
      message((-5, "pathreal: ++ %s type=|%s%s%s%s%s%s%s%s%s%s%s", path
            , (type & P_ABSOLUTE) ? "ABSOLUTE|" : state.null
            , (type & P_DOTDOT) ? "DOTDOT|" : state.null
            , (type & P_LSTAT) ? "LSTAT|" : state.null
            , st ? "MYSTAT|" : state.null
            , (type & P_NOOPAQUE) ? "NOOPAQUE|" : state.null
            , (type & P_NOSLASH) ? "NOSLASH|" : state.null
            , (type & P_PATHONLY) ? "PATHONLY|" : state.null
            , (type & P_READLINK) ? "READLINK|" : state.null
            , (type & P_SAFE) ? "SAFE|" : state.null
            , (type & P_SLASH) ? "SLASH|" : state.null
            , (type & P_TOP) ? "TOP|" : state.null
            ));
      path = _pathreal(path, type, st);
      message((-5, "pathreal: -- %s level=%d links=%d", path, state.path.level, state.path.nlinks));
      return path;
}

#undef      initialize
#define initialize()
#undef      pathreal
#define pathreal _pathreal
static

#endif

char*
pathreal(const char* apath, register int type, struct stat* st)
{
      char*             path = (char*)apath;
      register char*          sp;
      register char*          cp;
      register char*          ip;
      Table_t*          safe;
      int               oerrno = errno;
      int               opaqued = 0;
      int               len;
      int               vir;
      int               safesize;
      int               safe_dir;
      long              visits;
      char              buf[PATH_MAX + 1];

      static struct stat      stbuf;
      static struct stat      tsbuf;

      state.path.level = state.path.synthesize = state.path.nlinks = 0;
      if (!path)
      {
            errno = EFAULT;
            return 0;
      }
      initialize();
      if (state.in_2d)
      {
            if (!st || (!state.level || *path == '/') && !LSTAT(path, st))
                  return path;
            if (state.level && streq(path, ".") && !CHDIR(state.pwd))
            {
                  state.level = 0;
                  return path;
            }
            return 0;
      }
#if FS
      if (mounted() && (sp = fsreal(state.path.monitor, MSG_stat, state.path.mount)))
            apath = (char*)(path = sp);
#endif

      /*
       * handle null path, . and / separately
       */

      if (safe = state.safe ? &state.vsafe : (Table_t*)0)
      {
            type |= P_ABSOLUTE;
            if (!(safesize = state.safe->servicesize))
                  safesize = strlen(state.safe->service);
      }
      else
            type &= ~P_SAFE;
 again:
      if (!*path)
      {
            errno = ENOENT;
            return 0;
      }
      cp = sp = path;
      state.path.synthesize = state.path.linksize = 0;
      if (!st)
            st = &stbuf;

      /*
       * check if virtual dir has been created by another process
       * only P_PATHONLY|P_TOP calls (usually create or modify link) and
       * references to "." are checked for performance
       */

      if (state.level > 0 && state.pwd && ((type & (P_PATHONLY|P_TOP)) && *sp != '/' || *sp == '.' && sp[1] == 0))
      {
            if (!CHDIR(state.pwd))
                  state.level = 0;
            else if (!(type & (P_PATHONLY|P_TOP)))
            {
                  len = 0;
                  state.path.level += (state.path.synthesize = state.level);
                  sp = strcpy(state.path.name, state.pwd);
                  goto skip;
            }
      }
      if (!state.pwd || sp[1] == 0 && (*sp == '.' || *sp == '/' && !safe))
      {
            if (st != &stbuf && LSTAT(sp, st))
                  return 0;
            if (*sp == '/' || !state.pwd && (type & P_PATHONLY))
                  strncpy(state.path.name, sp, PATH_MAX);
            else if (!state.pwd)
            {
                  /*
                   * treat the current directory as if were empty
                   */

                  errno = ENOENT;
                  return 0;
            }
            else
                  strncpy(state.path.name, state.pwd, PATH_MAX);
            errno = oerrno;
            return state.path.name;
      }

      /*
       * put absolute pathname into state.path
       */

      safe_dir = 0;
      if (*path != '/')
      {
            strcpy(state.path.name, state.pwd);
            sp = state.path.name + state.pwdsize;
            *sp++ = '/';
            if (safe && state.pwdsize >= safesize && !strncmp(state.pwd, state.safe->service, safesize) && (!state.pwd[safesize] || state.pwd[safesize] == '/'))
                  safe_dir = safesize;
      }
      else
            sp = state.path.name;
      ip = state.path.name + elementsof(state.path.name);
      while (sp < ip && (*sp = *cp++))
            sp++;
      if (type & P_DOTDOT)
            strcpy(sp, "/..");
      sp = state.path.name;
      if (!(ip = pathcanon(sp + safe_dir, 0)))
      {
            errno = ENOENT;
            return 0;
      }
      if (type & (P_DOTDOT|P_NOSLASH))
      {
            /*
             * remove trailing slashes
             */

            while (*--ip == '/');
            *++ip = 0;
      }
      else if ((type & P_SLASH) && *(ip - 1) != '/')
            *ip++ = '/';
      if (*(ip - 1) == '/' && ip - sp > 1)
      {
            /*
             * trailing slash is equivalent to trailing slash-dot
             * this forces the common-sense interpretation
             */
#if DEBUG
            if (!(state.test & 010))
#endif
            *ip++ = '.';
            *ip = 0;
      }
      len = ip - sp;

      /*
       * try to use relative path
       */

      if (!(type & (P_LSTAT|P_READLINK)))
      {
            for (ip = state.pwd; *ip && *ip == *sp++; ip++);
            if (*ip != 0 || *sp && *sp != '/' || state.level < 0)
                  sp = state.path.name;
            else
            {
                  state.path.level += (state.path.synthesize = state.level);
                  if (state.level && !(type & P_PATHONLY) && st == &stbuf)
                  {
                        sp = state.path.name;
                        len -= state.pwdsize;
                  }
                  else if (type & P_ABSOLUTE)
                        sp = state.path.name;
                  else if (*sp == '/')
                        sp++;
            }
            if (*sp == 0)
                  sp = state.dot;
      }
 skip:
      if ((type & P_NOOPAQUE) && !LSTAT(sp, st) && checkopaque(sp, st))
      {
            message((-1, "%s: remove opaque", sp));
            UNLINK(sp);
            opaqued = 1;
      }
      if (safe && *sp == '/')
      {
            state.path.table = safe;
            cp = pathnext(sp, NiL, NiL);
            state.path.table = safe = 0;
            if (cp)
            {
                  state.path.level = 0;
                  path = strcpy(buf, sp);
                  message((-5, "pathreal: == safe map %s", path));
                  type &= ~(P_DOTDOT|P_SAFE);
                  goto again;
            }
            if (!*(sp + 1))
            {
                  strncpy(sp, state.safe->service, safesize);
                  sp[safesize] = 0;
            }
            else if (strncmp(sp, state.safe->service, safesize) || sp[safesize] && sp[safesize] != '/')
            {
                  if (*path != '/' && safe_dir)
                  {
                        errno = EPERM;
                        return 0;
                  }
                  if (sp[1])
                        strcpy(buf, sp);
                  else
                        *buf = 0;
                  len = sfsprintf(sp, sizeof(state.path.name), "%-*s%s", safesize, state.safe->service, buf);
                  message((-5, "pathreal: == safe next %s", sp));
                  if (!pathnext(sp, NiL, NiL))
                  {
                        errno = EPERM;
                        return 0;
                  }
            }
            else
                  type &= ~P_SAFE;
      }
      if ((type & P_SAFE) && state.path.level)
      {
            errno = EPERM;
            return 0;
      }
      if (type & P_PATHONLY)
      {
            errno = oerrno;
            return sp;
      }
      visits = 0;
      vir = 1;
      while (LSTAT(sp, st))
      {
            if (vir)
            {
                  if (apath[0] == '.' && apath[1] == '.' && apath[2] == '.' && !apath[3])
                  {
                        if (state.level > 0)
                        {
                              message((-1, "pathreal: %s => %s", apath, sp));
                              LSTAT(".", st);
                              return sp;
                        }
                        errno = ENOENT;
                        return 0;
                  }
                  vir = 0;
            }
            if (errno == ENOTDIR)
            {
                  /*
                   * check for version instance
                   */

                  cp = ip = sp + strlen(sp);
                  while (ip > sp && *--ip != '/');
                  if (ip < sp)
                        return 0;
                  while (ip > sp && *--ip == '/');
                  if (ip < sp)
                        return 0;
                  while (ip > sp && *--ip != '/');
                  if (*ip == '/')
                        ip++;
                  while (cp >= ip)
                  {
                        cp[4] = *cp;
                        cp--;
                  }
                  memcpy(ip, state.opaque, 4);
                  if (!LSTAT(sp, st))
                        break;
                  errno = ENOTDIR;
                  return 0;
            }

            if (errno != ENOENT || opaqued)
                  return 0;
#if FS
            /*
             * check user mount
             */

            if (visits)
            {
                  Mount_t*    mp;
                  const char* up;

                  if ((mp = getmount(sp, &up)) && (mp->fs->flags & FS_NAME) && (sp = fsreal(mp, MSG_open, (char*)up)) && !LSTAT(sp, st))
                        break;
            }
#endif

            /*
             * search down the viewpath
             */

            if (type & P_SAFE)
            {
                  errno = EPERM;
                  return 0;
            }
            if (!pathnext(state.path.name, NiL, &visits))
                  return 0;
            sp = state.path.name;
            if (!(type & P_ABSOLUTE))
            {
                  /*
                   * try to use relative path
                   */

                  for (ip = state.pwd; *ip && *ip == *sp++; ip++);
                  if (*ip == 0 && *sp == '/')
                        sp++;
                  else
                        sp = state.path.name;
            }
            if (*sp == 0)
                  sp = state.dot;
      }
      if (st->st_nlink > 1 && checkopaque(sp, st))
            return 0;
      if ((type & P_TOP) && state.path.level)
      {
            int   rfd;
            int   wfd;

            if ((rfd = OPEN(sp, O_RDONLY, 0)) < 0)
                  sp = 0;
            else
            {
                  tsbuf = *st;
                  wfd = open(apath, O_WRONLY|O_CREAT|O_TRUNC, st->st_mode & S_IPERM);
                  *st = tsbuf;
                  if (wfd < 0)
                        sp = 0;
                  else 
                  {
                        if (fs3d_copy(rfd, wfd, st))
                              sp = 0;
                        CLOSE(wfd);
                  }
                  CLOSE(rfd);
            }
            if (!sp)
            {
                  errno = EROFS;
                  return 0;
            }
            if (st == &stbuf)
                  st = 0;
            return pathreal(apath, P_PATHONLY, st);
      }
      IVIEW(st, state.path.level);
      if (state.path.synthesize)
      {
            if (state.path.level < state.level)
            {
                  if (len)
                  {
                        ip  = state.path.name + strlen(state.path.name) - len;
                        len = *ip;
                        *ip = 0;
                  }
                  if (!CHDIR(state.path.name))
                        state.level = state.path.level;
                  message((-1, "chdir=%s level=%d", state.path.name, state.level));
                  *ip = len;
            }
            else if (S_ISDIR(st->st_mode))
            {
                  int         mask;
                  static int  uid = -1;
                  static int  gid;

                  umask(mask = umask(0));
                  st->st_mode = (st->st_mode | (S_IRWXU|S_IRWXG|S_IRWXO)) & ~(mask & (S_IRWXU|S_IRWXG|S_IRWXO));
                  if (uid == -1)
                  {
                        uid = geteuid();
                        gid = getegid();
                  }
                  st->st_uid = uid;
                  st->st_gid = gid;
            }
      }
      ip = sp;

      /*
       * symbolic links handled specially
       * get filename from pathname
       */

      if (S_ISLNK(st->st_mode) && (len = checklink(sp, st, type)) > 1 && !(type & (P_LSTAT|P_READLINK)) && state.path.nlinks++ < MAXSYMLINKS)
      {
            path = strcpy(buf, state.path.name);
            message((-1, "pathreal: == again %s", path));
            if (*path != '/')
                  state.path.level = 0;
            type &= ~(P_DOTDOT|P_SAFE);
            goto again;
      }
#if VCS && defined(VCS_REAL)
      VCS_REAL(state.path.name, st);
#endif
      errno = oerrno;
      return sp;
}

/*
 * check whether sp points to a version object and find version instance
 * sp is canonicalized and points into state.path
 * when called from unlink, (type & P_PATHONLY) is set
 *     -1 for non-existent link
 *     length of path for a relative link that is not a version object
 *     0  otherwise
 *    state.path.linkname and state.path.linksize are set for version object
 */

int
checklink(const char* asp, struct stat* st, int type)
{
      register char*    sp = (char*)asp;
      register char*    ip;
      register int      len;
      register int      n;
      register char*    bp;

      char        buf[PATH_MAX + 1];

      if (sp < state.path.name || sp >= state.path.name + sizeof(state.path.name))
      {
            message((-1, "AHA#%d checklink bounds sp=%p state.path.name=%p sp=%s", __LINE__, sp, state.path.name, sp));
            sp = strncpy(state.path.name, sp, sizeof(state.path.name) - 1);
      }
      while (S_ISLNK(st->st_mode))
      {
            /*
             * go to the last component
             */

            if (ip = strrchr(sp, '/'))
                  ip++;
            else
                  ip = sp;
            strcpy(buf, ip);
            len = (state.path.name + sizeof(state.path.name) - 1) - ip;
            if ((len = READLINK(sp, ip, len)) < 0)
            {
                  message((-1, "%s: cannot readlink", sp));
                  return 0;
            }
            state.path.linkname = ip;
            state.path.linksize = len;

            /*
             * check for relative link
             */

            if (*ip != '/')
            {
                  ip[len] = 0;
                  if (*ip == *state.opaque && !memcmp(ip, state.opaque, 4) && !memcmp(ip + 4, buf, n = strlen(buf)))
                  {
                        /*
                         * version object
                         */

                        ip += n + 4;
                        if (instance(state.path.name, ip, st, 0))
                        {
                              state.path.linksize = strlen(state.path.linkname);
                              if (type & P_LSTAT)
                              {
                                    st->st_size = state.path.linksize;
                                    st->st_mode &= S_IPERM;
#ifdef S_IFLNK
                                    st->st_mode |= S_IFLNK;
#endif
                                    return 0;
                              }
                              continue;
                        }
                        errno = ENOENT;
                        return -1;
                  }
                  else if (!(type & (P_LSTAT|P_PATHONLY|P_READLINK)) && *ip == '.' && *(ip + 1) == '.' && (*(ip + 2) == '/' || *(ip + 2) == 0))
                  {
                        memcpy(buf, ip, len + 1);
                        bp = state.path.name;
                        while (ip > bp && *(ip - 1) == '/')
                              ip--;
                        for (;;)
                        {
                              *(sp = ip) = 0;
                              while (ip > bp && *--ip != '/');
                              while (ip > bp && *(ip - 1) == '/')
                                    ip--;
                              if (*ip == '/')
                                    ip++;
                              if ((n = READLINK(state.path.name, ip, PATH_MAX - (ip - state.path.name))) <= 0)
                              {
                                    *sp++ = '/';
                                    state.path.linkname = (char*)memcpy(sp, buf, len + 1);
                                    return sp + len - state.path.name;
                              }
                              if (*ip == '/')
                                    ip = (char*)memcpy(bp = state.path.name, ip, n);
                              else if (ip > bp)
                                    *(ip - 1) = '/';
                              ip += n;
                        }
                  }
            }

            /*
             * restore last component
             */

            if (!(type & P_READLINK))
                  strcpy(ip, buf);
            break;
      }
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index