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

file.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1987-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 Bell Laboratories
 *
 * pax file support
 */

#include "pax.h"

/*
 * "nocomment" is a hardwired "nocom"
 * should be an sfio discipline
 */

#include "nocomment.c"

#if __STDC__
#define chmod(a,b)      (error(-1,"%s#%d: chmod(%s,%05o)",__FILE__,__LINE__,a,b),chmod(a,b))
#endif

/*
 * return read file descriptor for filtered current input file
 */

int
apply(register Archive_t* ap, register File_t* f, Filter_t* fp)
{
      register int      n;
      char*       arg;
      int         rfd;
      int         wfd;

      if (state.filter.line <= 0)
            arg = f->path;
      else if (!*(arg = state.filter.command))
      {
            if ((rfd = open(f->st->st_size ? f->path : "/dev/null", O_RDONLY|O_BINARY)) < 0)
                  error(ERROR_SYSTEM|2, "%s: cannot read", f->path);
            return rfd;
      }
      message((-4, "filter: %s %s", fp->command, f->path));
      if ((wfd = open(state.tmp.file, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR)) < 0)
      {
            error(2, "%s: cannot create filter temporary %s", f->path, state.tmp.file);
            return -1;
      }
      if ((rfd = open(state.tmp.file, O_RDONLY|O_BINARY)) < 0)
      {
            error(2, "%s: cannot open filter temporary %s", f->path, state.tmp.file);
            close(wfd);
            if (remove(state.tmp.file))
                  error(1, "%s: cannot remove filter temporary %s", f->path, state.tmp.file);
            return -1;
      }
      if (remove(state.tmp.file))
            error(1, "%s: cannot remove filter temporary %s", f->path, state.tmp.file);
      if (ap->format->checksum)
            f->checksum = 0;
      f->st->st_size = 0;
      if (streq(*fp->argv, "nocomment"))
      {
            int   errors = error_info.errors;
            off_t count;
            Sfio_t*     ip;
            Sfio_t*     op;

            if ((ip = sfopen(NiL, f->path, "r")) && (op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE)) && (count = nocomment(ip, op)) < 0)
                  error(2, "%s: %s: filter error", f->path, *fp->argv);
            if (ip)
            {
                  sfclose(ip);
                  if (op)
                        sfclose(op);
                  else
                        error(2, "%s: cannot redirect filter", f->path);
            }
            else
                  error(2, "%s: cannot read", f->path);
            if (errors != error_info.errors)
            {
                  close(rfd);
                  close(wfd);
                  return -1;
            }
            f->st->st_size = count;
      }
      else
      {
            Proc_t*           proc;

            *fp->patharg = arg;
            if (!(proc = procopen(*fp->argv, fp->argv, NiL, NiL, PROC_READ)))
            {
                  error(2, "%s: cannot execute filter %s", f->path, *fp->argv);
                  close(rfd);
                  close(wfd);
                  return -1;
            }
            holeinit(wfd);
            while ((n = read(proc->rfd, state.tmp.buffer, state.buffersize)) > 0)
            {
                  if (holewrite(wfd, state.tmp.buffer, n) != n)
                  {
                        error(2, "%s: filter write error", f->path);
                        break;
                  }
                  if (ap->format->checksum)
                        f->checksum = (*ap->format->checksum)(&state, ap, f, state.tmp.buffer, n, f->checksum);
                  f->st->st_size += n;
            }
            holedone(wfd);
            if (n < 0)
                  error(ERROR_SYSTEM|2, "%s: %s filter read error", f->path, *fp->argv);
            if (n = procclose(proc))
                  error(2, "%s: %s filter exit code %d", f->path, *fp->argv, n);
      }
      close(wfd);
      message((-1, "%s: filter file size = %ld", f->path, f->st->st_size));
      return rfd;
}

/*
 * return read file descriptor for current input file
 */

int
openin(register Archive_t* ap, register File_t* f)
{
      register int      n;
      Filter_t*   fp;
      int         rfd;

      if (f->type != X_IFREG)
            rfd = -1;
      else if (fp = filter(ap, f))
            rfd = apply(ap, f, fp);
      else if ((rfd = open(f->st->st_size ? f->path : "/dev/null", O_RDONLY|O_BINARY)) < 0)
            error(ERROR_SYSTEM|2, "%s: cannot read", f->path);
      else if (ap->format->checksum)
      {
            f->checksum = 0;
            if (lseek(rfd, (off_t)0, SEEK_SET) != 0)
                  error(ERROR_SYSTEM|1, "%s: %s checksum seek error", f->path, ap->format->name);
            else
            {
                  while ((n = read(rfd, state.tmp.buffer, state.buffersize)) > 0)
                        f->checksum = (*ap->format->checksum)(&state, ap, f, state.tmp.buffer, n, f->checksum);
                  if (n < 0)
                        error(ERROR_SYSTEM|2, "%s: %s checksum read error", f->path, ap->format->name);
                  if (lseek(rfd, (off_t)0, SEEK_SET) != 0)
                        error(ERROR_SYSTEM|1, "%s: %s checksum seek error", f->path, ap->format->name);
            }
      }
      if (rfd < 0)
            f->st->st_size = 0;
      return rfd;
}

/*
 * create directory and all path name components leading to directory
 */

static int
missdir(register Archive_t* ap, register File_t* f)
{
      register char*    s;
      register char*    t;
      long        pp;
      struct stat*      st;
      struct stat*      sp;
      struct stat st0;
      struct stat st1;

      s = f->name;
      pathcanon(s, 0);
      if (t = strchr(*s == '/' ? s + 1 : s, '/'))
      {
            if (!state.mkdir)
            {
                  if (!state.warnmkdir)
                  {
                        state.warnmkdir = 1;
                        error(1, "omit the --nomkdir option to create intermediate directories");
                  }
                  return -1;
            }
            st = 0;
            sp = &st0;
            do
            {
                  *t = 0;
                  if (stat(s, sp))
                  {
                        *t = '/';
                        break;
                  }
                  *t = '/';
                  st = sp;
                  sp = (sp == &st0) ? &st1 : &st0;
            } while (t = strchr(t + 1, '/'));
            if (t)
            {
                  if (!st && stat(".", st = &st0))
                        error(ERROR_SYSTEM|3, "%s: cannot stat .", s);
                  pp = f->perm;
                  f->perm = st->st_mode & state.modemask;
                  sp = f->st;
                  f->st = st;
                  do
                  {
                        *t = 0;
                        if (mkdir(s, f->perm))
                        {
                              error(ERROR_SYSTEM|2, "%s: cannot create directory", s);
                              *t = '/';
                              f->perm = pp;
                              f->st = sp;
                              return -1;
                        }
                        setfile(ap, f);
                        *t = '/';
                  } while (t = strchr(t + 1, '/'));
                  f->perm = pp;
                  f->st = sp;
            }
      }
      return 0;
}

/*
 * open file for writing, set all necessary info
 */

int
openout(register Archive_t* ap, register File_t* f)
{
      register int      fd;
      int         exists;
      int         perm;
      struct stat st;

      pathcanon(f->name, 0);

      /*
       * if not found and state.update then check down the view
       *
       * NOTE: VPATH in app code is ugly but the benefits of the
       *     combination with state.update win over beauty
       */

      if (f->ro)
      {
            f->name = "PAX-INTERNAL-ERROR";
            f->skip = 1;
            exists = 0;
      }
      else if (exists = !(*state.statf)(f->name, &st))
      {
            if (!state.clobber && !S_ISDIR(st.st_mode))
            {
                  error(1, "%s: already exists -- not overwritten", f->name);
                  return -1;
            }
            f->chmod = f->perm != (st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) &&
                  (state.chmod || state.update || S_ISDIR(st.st_mode));
            st.st_mode = modex(st.st_mode);
      }
      else
      {
            typedef struct View
            {
                  struct View*      next;
                  char*       root;
                  dev_t       dev;
                  ino_t       ino;
            } View_t;

            View_t*                 vp;
            View_t*                 tp;
            char*             s;
            char*             e;

            static View_t*          view;
            static char*            offset;

            if (state.update && !offset)
            {
                  if (s = getenv("VPATH"))
                  {
                        if (!(s = strdup(s)))
                              nospace();
                        do
                        {
                              if (e = strchr(s, ':'))
                                    *e++ = 0;
                              if (!(vp = newof(0, View_t, 1, 0)))
                                    nospace();
                              vp->root = s;
                              if (stat(s, &st))
                              {
                                    vp->dev = 0;
                                    vp->ino = 0;
                              }
                              else
                              {
                                    vp->dev = st.st_dev;
                                    vp->ino = st.st_ino;
                              }
                              if (view)
                                    tp = tp->next = vp;
                              else
                                    view = tp = vp;
                        } while (s = e);
                        s = state.pwd;
                        e = 0;
                        for (;;)
                        {
                              if (stat(s, &st))
                                    error(ERROR_SYSTEM|3, "%s: cannot stat pwd component", s);
                              for (vp = view; vp; vp = vp->next)
                                    if (vp->ino == st.st_ino && vp->dev == st.st_dev)
                                    {
                                          offset = e ? e + 1 : ".";
                                          tp = view;
                                          view = vp->next;
                                          while (tp && tp != view)
                                          {
                                                vp = tp;
                                                tp = tp->next;
                                                free(vp);
                                          }
                                          if (e)
                                                *e = '/';
                                          goto found;
                                    }
                              if (e)
                                    *e = '/';
                              else
                                    e = s + strlen(s);
                              while (e > s && *--e != '/');
                              if (e <= s)
                                    break;
                              *e = 0;
                        }
                  }
            found:
                  if (!offset)
                        offset = ".";
            }
            st.st_mode = 0;
            st.st_mtime = 0;
            if (*f->name != '/')
                  for (vp = view; vp; vp = vp->next)
                  {
                        sfsprintf(state.tmp.buffer, state.tmp.buffersize - 1, "%s/%s/%s", vp->root, offset, f->name);
                        if (!stat(state.tmp.buffer, &st))
                              break;
                  }
            f->chmod = state.chmod || state.update;
      }
      if (f->delta.op == DELTA_delete)
      {
            if (exists)
                  switch (X_ITYPE(st.st_mode))
                  {
                  case X_IFDIR:
                        if (!f->ro)
                        {
                              if (rmdir(f->name))
                                    error(ERROR_SYSTEM|2, "%s: cannot remove directory", f->name);
                              else
                                    listentry(f);
                        }
                        break;
                  default:
                        if (remove(f->name))
                              error(ERROR_SYSTEM|2, "%s: cannot remove file", f->name);
                        else
                              listentry(f);
                        break;
                  }
            return -1;
      }
      if (state.operation == (IN|OUT))
      {
            if (exists && f->st->st_ino == st.st_ino && f->st->st_dev == st.st_dev)
            {
                  error(2, "attempt to pass %s to self", f->name);
                  return -1;
            }
            if (state.linkf && f->type != X_IFDIR && (state.linkf == pathsetlink || f->st->st_dev == state.dev))
            {
                  if (exists)
                        remove(f->name);
                  if ((*state.linkf)(f->path, f->name))
                  {
                        if (!exists && missdir(ap, f))
                        {
                              error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
                              return -1;
                        }
                        if (exists || (*state.linkf)(f->path, f->name))
                        {
                              error(ERROR_SYSTEM|2, "%s: cannot link to %s", f->path, f->name);
                              return -1;
                        }
                  }
                  setfile(ap, f);
                  return -2;
            }
      }
      if (prune(ap, f, &st))
            return -1;
      switch (f->type)
      {
      case X_IFDIR:
            if (!(ap->format->flags & KEEPSIZE))
                  f->st->st_size = 0;
            if (f->ro)
                  return -1;
            if (exists && X_ITYPE(st.st_mode) != X_IFDIR)
            {
                  if (remove(f->name))
                  {
                        error(ERROR_SYSTEM|2, "cannot remove current %s", f->name);
                        return -1;
                  }
                  exists = 0;
            }
            if (!exists && mkdir(f->name, f->perm) && (missdir(ap, f) || mkdir(f->name, f->perm)))
            {
                  error(ERROR_SYSTEM|2, "%s: cannot create directory", f->name);
                  return -1;
            }
            setfile(ap, f);
            if (!exists || f->chmod)
            {
                  listentry(f);
                  fd = -1;
            }
            else if (state.update && exists)
                  fd = -1;
            else
                  fd = -2;
            return fd;
      case X_IFLNK:
            if (!*f->linkpath)
                  return -2;
            if (streq(f->name, f->linkpath))
            {
                  error(1, "%s: symbolic link loops to self", f->name);
                  return -1;
            }
            if (exists && remove(f->name))
            {
                  error(ERROR_SYSTEM|2, "cannot remove current %s", f->name);
                  return -1;
            }
            if (pathsetlink(f->linkpath, f->name))
            {
                  if (!exists && missdir(ap, f))
                  {
                        error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
                        return -1;
                  }
                  if (exists || pathsetlink(f->linkpath, f->name))
                  {
                        error(ERROR_SYSTEM|2, "%s: cannot symlink to %s", f->name, f->linkpath);
                        return -1;
                  }
            }
            setfile(ap, f);
            listentry(f);
            return -1;
      case X_IFSOCK:
            IDEVICE(f->st, 0);
            /*FALLTHROUGH*/
      case X_IFBLK:
      case X_IFCHR:
      case X_IFIFO:
            if (!(ap->format->flags & KEEPSIZE))
                  f->st->st_size = 0;
            break;
      }
      if (!addlink(ap, f))
            return -1;
      switch (f->type)
      {
      case X_IFIFO:
            if (exists && remove(f->name))
            {
                  error(ERROR_SYSTEM|2, "cannot remove current %s", f->name);
                  return -1;
            }
            if (mkfifo(f->name, f->st->st_mode & S_IPERM))
            {
                  if (errno == EPERM)
                  {
                  nofifo:
                        error(ERROR_SYSTEM|2, "%s: cannot create fifo file", f->name);
                        return -1;
                  }
                  if (!exists && missdir(ap, f))
                  {
                        error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
                        return -1;
                  }
                  if (exists || mkfifo(f->name, f->st->st_mode & S_IPERM))
                        goto nofifo;
            }
            setfile(ap, f);
            return -2;
      case X_IFSOCK:
            IDEVICE(f->st, 0);
            /*FALLTHROUGH*/
      case X_IFBLK:
      case X_IFCHR:
            if (exists && remove(f->name))
            {
                  error(ERROR_SYSTEM|2, "cannot remove current %s", f->name);
                  return -1;
            }
            if (mknod(f->name, f->st->st_mode, idevice(f->st)))
            {
                  if (errno == EPERM)
                  {
                  nospecial:
                        error(ERROR_SYSTEM|2, "%s: cannot create %s special file", f->name, (f->type == X_IFBLK) ? "block" : "character");
                        return -1;
                  }
                  if (!exists && missdir(ap, f))
                  {
                        error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
                        return -1;
                  }
                  if (exists || mknod(f->name, f->st->st_mode, idevice(f->st)))
                        goto nospecial;
            }
            setfile(ap, f);
            return -2;
      default:
            error(1, "%s: unknown file type 0%03o -- creating regular file", f->name, f->type >> 12);
            /*FALLTHROUGH*/
      case X_IFREG:
            if (f->ro)
                  return dup(1);
            if (state.intermediate)
            {
                  char* d;
                  char* e;
                  int   n;
                  int   ifd;

                  /*
                   * copy to intermediate output file and rename
                   * to real file only on success - a handy
                   * backup option
                   *
                   * thanks to the amazing dr. ek
                   */

                  if (missdir(ap, f))
                  {
                        error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
                        return -1;
                  }
                  d = (e = strrchr(f->name, '/')) ? f->name : ".";
                  for (n = 0;; n++)
                  {
                        if (e)
                              *e = 0;
                        f->intermediate = pathtemp(ap->path.temp, sizeof(ap->path.temp), d, error_info.id, &ifd);
                        if (e)
                              *e = '/';
                        message((-4, "%s: intermediate %s", f->name, f->intermediate));
                        if (f->intermediate)
                        {
                              ap->errors = error_info.errors;
                              return ifd;
                        }
                        if (n)
                        {
                              error(ERROR_SYSTEM|2, "%s: cannot create intermediate name", f->name);
                              return -1;
                        }
                  }
            }

            /*
             * ok, the exists bits are only used right here
             * you do the defines if its that important
             *
             * <chmod u+w><remove><missdir>
             *    4     don't attempt
             *    2     attempted and succeeded
             *    1     attempted and failed
             */

            if (!exists)
                  exists |= 0440;
            else if (!state.linkf)
                  exists |= remove(f->name) ? 0010 : 0420;
            else if (st.st_mode & S_IWUSR)
                  exists |= 0400;
            if ((perm = f->perm) & (S_ISUID|S_ISGID|S_ISVTX))
                  perm &= ~(S_ISUID|S_ISGID|S_ISVTX);
            while ((fd = open(f->name, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, perm)) < 0)
            {
            again:
                  if (!(exists & 0007))
                  {
                        if (missdir(ap, f))
                        {
                              error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
                              return -1;
                        }
                        exists |= 0002;
                  }
                  else if (!(exists & 0700))
                  {
                        if (chmod(f->name, perm | S_IWUSR))
                        {
                              exists |= 0100;
                              goto again;
                        }
                        exists |= 0200;
                  }
                  else if (!(exists & 0070))
                  {
                        if (remove(f->name))
                        {
                              exists |= 0010;
                              goto again;
                        }
                        exists ^= 0620;
                  }
                  else
                  {
                        error(ERROR_SYSTEM|2, "%s: cannot create%s%s", f->name, (exists & 0100) ? ERROR_translate(0, 0, 0, ", cannot enable user write") : "", (exists & 0010) ? ERROR_translate(0, 0, 0, ", cannot remove") : "");
                        return -1;
                  }
            }
            if (perm != f->perm)
                  f->chmod = 1;
            else if ((exists & 0200) && chmod(f->name, f->perm))
                  error(ERROR_SYSTEM|1, "%s: cannot restore original mode %s", f->name, fmtperm(st.st_mode & S_IPERM));
            return fd;
      }
}

/*
 * close an openout() fd, doing the intermediate rename if needed
 */

int
closeout(register Archive_t* ap, register File_t* f, int fd)
{
      register char*    s;
      int         r;

      r = 0;
      if (state.sync && fsync(fd))
            r = -1;
      if (close(fd))
            r = -1;
      if (s = f->intermediate)
      {
            f->intermediate = 0;
            if (ap->errors != error_info.errors)
            {
                  if (remove(s))
                        error(ERROR_SYSTEM|2, "%s: cannot remove intermediate file %s", f->name, s);
                  return -1;
            }
            if (rename(s, f->name) && (remove(f->name) || rename(s, f->name)))
            {
                  error(ERROR_SYSTEM|2, "%s: cannot rename from intermediate file %s", f->name, s);
                  return -1;
            }
            if (chmod(f->name, f->perm))
            {
                  error(ERROR_SYSTEM|1, "%s: cannot change mode to %s", f->name, fmtperm(f->perm));
                  return -1;
            }
      }
      return r;
}

/*
 * get file info for output
 */

int
getfile(register Archive_t* ap, register File_t* f, register Ftw_t* ftw)
{
      register char*          name;
      register int            n;
      char*             e;

      name = ftw->path;
      message((-4, "getfile(%s)", name));
      switch (ftw->info)
      {
      case FTW_NS:
            error(2, "%s: not found", name);
            return 0;
      case FTW_DNR:
            if (state.files)
                  error(2, "%s: cannot read directory", name);
            break;
      case FTW_D:
      case FTW_DNX:
      case FTW_DP:
            if (!state.descend)
                  ftw->status = FTW_SKIP;
            else if (ftw->info == FTW_DNX)
            {
                  error(2, "%s: cannot search directory", name);
                  ftw->status = FTW_SKIP;
            }
            else if (!state.files)
            {
                  /*
                   * stdin files most likely come from tw/find with
                   * directory descendents already included; in posix
                   * omitting -d would result in duplicate output copies
                   * so we avoid the problem by peeking ahead and
                   * pruning all paths with this dir prefix
                   */

                  n = ftw->pathlen;
                  name = stash(&ap->path.peek, name, n);
                  name[n] = '/';
                  if (!state.peekfile || !strncmp(state.peekfile, name, n))
                        while (state.peekfile = sfgetr(sfstdin, '\n', 1))
                              if (strncmp(state.peekfile, name, n))
                              {
                                    state.peeklen = sfvalue(sfstdin) - 1;
                                    break;
                              }
                  name[n] = 0;
            }
            break;
      }
      if (ap->delta)
            ap->delta->hdr = ap->delta->hdrbuf;
      name = stash(&ap->path.name, name, ftw->pathlen);
      pathcanon(name, 0);
      f->path = stash(&ap->path.path, name, ftw->pathlen);
      f->name = map(name);
      if (state.files && state.operation == (IN|OUT) && dirprefix(state.destination, name))
            return 0;
      f->namesize = strlen(f->name) + 1;
      ap->st = ftw->statb;
      f->st = &ap->st;
      f->perm = f->st->st_mode & S_IPERM;
      f->st->st_mode = modex(f->st->st_mode);
      f->uidname = 0;
      f->gidname = 0;
      f->link = 0;
      if ((f->type = X_ITYPE(f->st->st_mode)) == X_IFLNK)
      {
            f->linkpathsize = f->st->st_size + 1;
            f->linkpath = stash(&ap->stash.link, NiL, f->linkpathsize);
            if (pathgetlink(f->path, f->linkpath, f->linkpathsize) != f->st->st_size)
            {
                  error(2, "%s: cannot read symbolic link", f->path);
                  ftw->status = FTW_SKIP;
                  return 0;
            }
            f->linktype = SOFTLINK;
            pathcanon(f->linkpath, 0);
            if (!(state.ftwflags & FTW_PHYSICAL))
                  f->linkpath = map(f->linkpath);
            if (streq(f->path, f->linkpath))
            {
                  error(2, "%s: symbolic link loops to self", f->path);
                  ftw->status = FTW_SKIP;
                  return 0;
            }
      }
      else
      {
            f->linktype = NOLINK;
            f->linkpath = 0;
            f->linkpathsize = 0;
      }
      f->ro = ropath(f->name);
      if (!validout(ap, f))
            return 0;
      if (!(state.operation & IN) && f->type != X_IFDIR)
      {
            if (!addlink(ap, f) && !state.header.linkdata)
                  f->st->st_size = 0;
            message((-4, "getfile(%s): dev'=%d ino'=%d", f->name, f->st->st_dev, f->st->st_ino));
      }
      ap->entries++;
      f->delta.op = 0;
      f->longname = 0;
      f->longlink = 0;
      f->skip = 0;
      if (state.mode)
      {
            f->st->st_mode = strperm(state.mode, &e, f->st->st_mode);
            if (*e)
                  error(2, "%s: invalid mode expression", state.mode);
      }
      message((-2, "getfile(): path=%s name=%s mode=%s size=%I*d", name, f->name, fmtmode(f->st->st_mode, 1), sizeof(f->st->st_size), f->st->st_size));
      return 1;
}

/*
 * check that f is valid for archive output
 */

int
validout(register Archive_t* ap, register File_t* f)
{
      if (f->ro)
            return 0;
      switch (f->type)
      {
      case X_IFBLK:
      case X_IFCHR:
            f->st->st_size = 0;
            break;
      case X_IFREG:
            IDEVICE(f->st, 0);
            break;
      case X_IFDIR:
      case X_IFIFO:
      case X_IFLNK:
            f->st->st_size = 0;
            IDEVICE(f->st, 0);
            break;
      }
      return ap->format->validate ? ((*ap->format->validate)(&state, ap, f) > 0) : 1;
}

/*
 * add file which may be a link
 * 0 returned if <dev,ino> already added
 */

int
addlink(register Archive_t* ap, register File_t* f)
{
      register Link_t*  p;
      register char*          s;
      int               n;
      Fileid_t          id;
      unsigned short          us;

      id.dev = f->st->st_dev;
      id.ino = f->st->st_ino;
      if (!ap->delta)
            switch (state.operation)
            {
            case IN:
                  us = id.dev;
                  if (us > state.devcnt)
                  {
                        state.devcnt = us;
                        state.inocnt = id.ino;
                  }
                  else if (us == state.devcnt)
                  {
                        us = id.ino;
                        if (us > state.inocnt)
                              state.inocnt = us;
                  }
                  break;
            case IN|OUT:
                  if (!state.pass)
                        break;
                  /*FALLTHROUGH*/
            case OUT:
                  if (!++state.inocnt)
                  {
                        if (!++state.devcnt)
                              goto toomany;
                        state.inocnt = 1;
                  }
                  f->st->st_dev = state.devcnt;
                  f->st->st_ino = state.inocnt;
                  break;
            }
      if (f->type == X_IFDIR)
            return 0;
      if (ap->format->flags & NOHARDLINKS)
      {
            if (state.operation == IN || f->st->st_nlink <= 1)
                  return 1;
      }
      else if ((ap->format->flags & LINKTYPE) && state.operation == IN)
      {
            if (f->linktype == NOLINK)
                  return 1;
            f->linkpath = map(f->linkpath);
            goto linked;
      }
      else if (f->st->st_nlink <= 1)
            return 1;
      if (p = (Link_t*)hashget(state.linktab, (char*)&id))
      {
            if (ap->format->flags & NOHARDLINKS)
            {
                  error(1, "%s: hard link information lost in %s format", f->name, ap->format->name);
                  return 1;
            }
            f->st->st_dev = p->id.dev;
            f->st->st_ino = p->id.ino;
            f->link = p;
            f->linktype = HARDLINK;
            f->linkpath = p->name;
            if (state.pass && (state.operation & OUT) || !state.pass && state.operation == OUT)
                  return 0;
      linked:
            message((-1, "addlink(%s,%s)", f->name, f->linkpath));
            if (ap->format->event && (ap->format->events & PAX_EVENT_BUG_19951031))
                  (*ap->format->event)(&state, ap, f, NiL, PAX_EVENT_BUG_19951031);
            if (streq(f->name, f->linkpath))
            {
                  error(2, "%s: hard link loops to self", f->name);
                  return 0;
            }
            if (!state.list)
            {
                  s = f->linkpath;
                  if (access(s, F_OK))
                  {
                        f->skip = 1;
                        error(2, "%s must exist for hard link %s", s, f->name);
                        return 0;
                  }
                  remove(f->name);
                  if (state.operation == IN && *s != '/')
                  {
                        strcpy(state.pwd + state.pwdlen, s);
                        s = state.pwd;
                  }
                  if (link(s, f->name))
                  {
                        if (missdir(ap, f))
                        {
                              error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name);
                              return 0;
                        }
                        if (link(s, f->name))
                        {
                              error(ERROR_SYSTEM|2, "%s: cannot link to %s", f->linkpath, f->name);
                              return -1;
                        }
                  }
                  listentry(f);
            }
            return 0;
      }
      n = strlen(f->name) + 1;
      if (!(p = newof(0, Link_t, 1, n)))
            goto toomany;
      f->link = p;
      strcpy(p->name = (char*)p + sizeof(*p), f->name);
      p->namesize = n;
      p->id.dev = f->st->st_dev;
      p->id.ino = f->st->st_ino;
      hashput(state.linktab, NiL, p);
      return -1;
 toomany:
      if (!state.warnlinknum)
      {
            state.warnlinknum = 1;
            error(1, "too many hard links -- some links may become copies");
      }
      return -1;
}

/*
 * get file uid and gid names given numbers
 */

void
getidnames(register File_t* f)
{
      if (!f->uidname)
            f->uidname = fmtuid(f->st->st_uid);
      if (!f->gidname)
            f->gidname = fmtgid(f->st->st_gid);
}

/*
 * set file uid and gid numbers given names
 */

void
setidnames(register File_t* f)
{
      register int      id;

      if (f->uidname)
      {
            if ((id = struid(f->uidname)) < 0)
            {
                  if (id == -1 && state.owner)
                        error(1, "%s: invalid user name", f->uidname);
                  f->uidname = 0;
                  id = state.uid;
            }
            f->st->st_uid = id;
      }
      if (f->gidname)
      {
            if ((id = strgid(f->gidname)) < 0)
            {
                  if (id == -1 && state.owner)
                        error(1, "%s: invalid group name", f->gidname);
                  f->gidname = 0;
                  id = state.gid;
            }
            f->st->st_gid = id;
      }
}

/*
 * allocate and initialize new archive pointer
 */

Archive_t*
initarchive(const char* name, int mode)
{
      Archive_t*  ap;

      if (!(ap = newof(0, Archive_t, 1, 0)))
            nospace();
      initfile(ap, &ap->file, &ap->st, NiL, 0);
      ap->name = (char*)name;
      ap->expected = ap->format = 0;
      ap->section = 0;
      ap->sum = -1;
      ap->mio.mode = ap->tio.mode = mode;
      ap->io = &ap->mio;
      return ap;
}

/*
 * return pointer to archive for op
 */

Archive_t*
getarchive(int op)
{
      Archive_t** app;

      app = (op & OUT) ? &state.out : &state.in;
      if (!*app)
            *app = initarchive(NiL, (op & OUT) ? (state.append ? (O_WRONLY|O_APPEND|O_CREAT) : (O_CREAT|O_TRUNC|O_WRONLY)) : O_RDONLY);
      return *app;
}

/*
 * initialize file info with name and mode
 */

void
initfile(register Archive_t* ap, register File_t* f, struct stat* st, register char* name, int mode)
{
      memzero(f, sizeof(*f));
      f->st = st;
      memzero(f->st, sizeof(*f->st));
      if (name)
      {
            f->id = f->name = f->path = name;
            f->namesize = strlen(name) + 1;
      }
      f->st->st_mode = modex(mode);
      f->st->st_nlink = 1;          /* system V needs this!!! */
}

/*
 * set copied file info
 */

void
setfile(register Archive_t* ap, register File_t* f)
{
      register Post_t*  p;
      Post_t                  post;

      if (f->skip || f->extended)
            return;
      switch (f->type)
      {
      case X_IFLNK:
#if _lib_lchown
            if (state.owner)
            {
                  if (state.flags & SETIDS)
                  {
                        post.uid = state.setuid;
                        post.gid = state.setgid;
                  }
                  else
                  {
                        post.uid = f->st->st_uid;
                        post.gid = f->st->st_gid;
                  }
                  if (lchown(f->name, post.uid, post.gid) < 0)
                        error(1, "%s: cannot chown to (%d,%d)", f->name, post.uid, post.gid);
            }
#endif
#if _lib_lchmod
            if (f->chmod)
            {
                  int         m;
                  struct stat st;

                  if (lchmod(f->name, f->perm & state.modemask))
                        error(1, "%s: cannot chmod to %s", f->name, fmtmode(f->perm & state.modemask, 0) + 1);
                  else if (m = f->perm & (S_ISUID|S_ISGID|S_ISVTX))
                  {
                        if (lstat(f->name, &st))
                              error(1, "%s: not found", f->name);
                        else if (m ^= (st.st_mode & (S_ISUID|S_ISGID|S_ISVTX)))
                              error(1, "%s: mode %s not set", f->name, fmtmode(m, 0) + 1);
                  }
            }
#endif
            return;
      case X_IFDIR:
            if (f->chmod || state.acctime || state.modtime || state.owner || (f->perm & S_IRWXU) != S_IRWXU)
            {
                  if (!(p = newof(0, Post_t, 1, 0)))
                        error(3, "not enough space for file restoration info");
                  tvgetatime(&p->atime, f->st);
                  tvgetmtime(&p->mtime, f->st);
                  p->uid = f->st->st_uid;
                  p->gid = f->st->st_gid;
                  p->mode = f->perm;
                  if ((f->perm & S_IRWXU) != S_IRWXU)
                  {
                        p->chmod = 1;
                        if (chmod(f->name, f->perm|S_IRWXU))
                              error(1, "%s: cannot chmod to %s", f->name, fmtmode(f->st->st_mode|X_IRWXU, 1) + 1);
                  }
                  else
                        p->chmod = f->chmod;
                  hashput(state.restore, f->name, p);
                  return;
            }
            break;
      }
      p = &post;
      tvgetatime(&p->atime, f->st);
      tvgetmtime(&p->mtime, f->st);
      p->uid = f->st->st_uid;
      p->gid = f->st->st_gid;
      p->mode = f->perm;
      p->chmod = f->chmod;
      restore(f->name, (char*)p, NiL);
}

/*
 * set access and modification times of file
 */

void
settime(const char* name, Tv_t* ap, Tv_t* mp, Tv_t* cp)
{
      if (*name && tvtouch(name, ap, mp, cp, 1))
            error(1, "%s: cannot set times", name);
}

/*
 * restore file status after processing
 */

int
restore(register const char* name, char* ap, void* handle)
{
      register Post_t*  p = (Post_t*)ap;
      int               m;
      struct stat       st;

      NoP(handle);
      if (!*name)
            return 0;
      if (state.owner)
      {
            if (state.flags & SETIDS)
            {
                  p->uid = state.setuid;
                  p->gid = state.setgid;
            }
            if (chown(name, p->uid, p->gid) < 0)
                  error(1, "%s: cannot chown to (%d,%d)", name, p->uid, p->gid);
      }
      if (p->chmod)
      {
            if (chmod(name, p->mode & state.modemask))
                  error(1, "%s: cannot chmod to %s", name, fmtmode(p->mode & state.modemask, 0) + 1);
            else if (m = p->mode & (S_ISUID|S_ISGID|S_ISVTX))
            {
                  if (stat(name, &st))
                        error(1, "%s: not found", name);
                  else if (m ^= (st.st_mode & (S_ISUID|S_ISGID|S_ISVTX)))
                        error(1, "%s: mode %s not set", name, fmtmode(m, 0) + 1);
            }
      }
      if (state.modtime)
            settime(name, &p->atime, &p->mtime, NiL);
      return 0;
}

/*
 * return 1 if f output can be pruned
 */

int
prune(register Archive_t* ap, register File_t* f, register struct stat* st)
{
      if (st->st_mode == f->st->st_mode && (ap->delta && f->st->st_mtime == st->st_mtime || state.update && (unsigned long)f->st->st_mtime <= (unsigned long)st->st_mtime))
            return 1;
      return 0;
}

/*
 * write siz bytes of buf to fd checking for HOLE_MIN hole chunks
 * we assume siz is rounded nicely until the end
 */

ssize_t
holewrite(int fd, void* buf, size_t siz)
{
      register char*    t = (char*)buf;
      register char*    e = t + siz;
      register char*    b = 0;
      register char*    s;
      ssize_t           i;
      ssize_t           n = 0;

      static char hole[HOLE_MIN];

#if DEBUG
      if (state.test & 0000100)
            b = t;
      else
#endif
      while (t < e)
      {
            s = t;
            if ((t += HOLE_MIN) > e)
                  t = e;
            if (!*s && !*(t - 1) && !memcmp(s, hole, t - s))
            {
                  if (b)
                  {
                        if (state.hole)
                        {
                              if (lseek(fd, state.hole, SEEK_CUR) < state.hole)
                                    return -1;
                              state.hole = 0;
                        }
                        if ((i = write(fd, b, s - b)) != (s - b))
                              return i;
                        n += i;
                        b = 0;
                  }
                  state.hole += t - s;
                  n += t - s;
            }
            else if (!b)
                  b = s;
      }
      if (b)
      {
            if (state.hole)
            {
                  if (lseek(fd, state.hole, SEEK_CUR) < state.hole)
                        return -1;
                  state.hole = 0;
            }
            if ((i = write(fd, b, e - b)) != (e - b))
                  return i;
            n += i;
      }
      return n;
}

/*
 * make a seekable copy of ap->io input
 */

void
seekable(Archive_t* ap)
{
      off_t       m;
      off_t       z;
      char*       s;
      int         rfd;
      int         wfd;

      if ((wfd = open(state.tmp.file, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, S_IRUSR)) < 0)
            error(ERROR_SYSTEM|3, "%s: cannot create seekable temporary %s", ap->name, state.tmp.file);
      if ((rfd = open(state.tmp.file, O_RDONLY|O_BINARY)) < 0)
            error(ERROR_SYSTEM|3, "%s: cannot open seekable temporary %s", ap->name, state.tmp.file);
      if (remove(state.tmp.file))
            error(ERROR_SYSTEM|1, "%s: cannot remove seekable temporary %s", ap->name, state.tmp.file);
      ap->io->seekable = 1;
      z = 0;
      s = ap->io->buffer + MAXUNREAD;
      m = ap->io->last - s;
      do
      {
            if (write(wfd, s, m) != m)
                  error(ERROR_SYSTEM|3, "%s: seekable temporary %s write error", ap->name, state.tmp.file);
            z += m;
      } while ((m = read(ap->io->fd, s, state.buffersize)) > 0);
      close(wfd);
      close(ap->io->fd);
      ap->io->size = z;
      z = ap->io->count;
      ap->io->next = ap->io->last = s;
      ap->io->offset = ap->io->count = 0;
      if (ap->io->fd || (ap->io->fd = dup(rfd)) < 0)
            ap->io->fd = rfd;
      else
            close(rfd);
      bread(ap, NiL, z, z, 0);
}

Generated by  Doxygen 1.6.0   Back to index