SUMMARY: Changing UIDs & GIDs

From: Tom Mornini (tmornini@infomania.com)
Date: Tue Nov 08 1994 - 07:21:19 CST


> I have two indepent Sun machines (a 4/300 running 4.1.1 and a 630MP running
> 2.3 with current patches) that have been entirely independent up until now.

> I now need to transfer files via NFS from the 630MP to the 4/300. The UID's
> and GID's do not match. I would like to bring the 4/300 into conformity
with
> the 630, but fear the consequences of doing so.

> Has anyone been through this before? If I change the ID's, how do I get the
> existing files to follow the changes?

The answers mostly said that this was no problem, that a little care
needed to be used to avoid any hassles, and that some good understanding
for the find command was going to be a big plus during this operation.

Almost every response included find command lines that would handle the
job, and one respondent actually included the enclosed C code that he
used a while ago.

The general method quickly narrowed to:

   Make a new set of UIDs and GIDs. Use the find command to change all
   existing UIDs and GIDs to the new set. Then make a set of final UIDs
   and GIDs, and change the intermediate UIDs and GIDs to the final ones.

1) edit passwd and group to include intermediate IDs

2) find / -user <old UID> -exec chown <intermediate UID> {} \;
     find / -group <old GID> -exec chgrp <intermediate GID> {} \;

3) edit passwd and group to include final IDs and exclude old IDs

4) find / -user <intermediate UID> -exec chown <final UID> {} \;
     find / -group <intermediate GID> -exec chgrp <final GID> {} \;

5) edit passwd and group to exclude intermediate IDs.

6) reboot (to handle SET ID executables)

The pitfalls on this are:
   Being careful not to merge a couple of UIDs or GIDs since there is no
   way to fix this, and the system will likely be unusable in the interim,
   especially if you want to stay sane!

The best advice given was.
   Do level 0 dumps on all filesystems. [ Before you start :-) ]

Thank you very much to the following list of recipients!

----------

jerry.springer@valcom.com (Jerry Springer)
Glenn.Satchell@uniq.com.au (Glenn Satchell - Uniq Professional Services)
tom@uni-paderborn.de <Torsten Metzner>
peffley@indiana.nrlssc.navy.mil <Monty B. Peffley>
acker@se01.wg2.waii.com <Douglas L. Acker>
u2is9gef@crrel41.crrel.usace.army.mil (GREGOR E FELLERS )
jonh@hitl.washington.edu
stern@sunrise.East.Sun.COM (Hal Stern - NE Area Systems Engineer)
bern@penthesilea.uni-trier.de (Jochen Bern)
Leif Hedstrom <leif@infoseek.com>

-------------------------- S N I P ---------------------------------
/*
 * bchown.c: Batch chown, Leif Hedstrom (leif@infoseek.com)
 */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>

/*
 * Some definitions, compile with `-DSYSV' on a System VR4 machine.
 */
#define DELIMITER '-'
#define IDMAX 65535

#if (defined(SYSV) || defined(SVR4))
# define NAMELEN(ent) strlen(ent->d_name)
#else
# define NAMELEN(ent) ent->d_namlen
#endif

/*
 * Global variables.
 */
gid_t fgid[IDMAX];
uid_t fuid[IDMAX];
int verbose = 0;

/*
 * Initialize the GID/UID variables.
 */
void
init()
{
  int i;

  for (i = 0; i < IDMAX; i++)
    fgid[i] = fuid[i] = i;
}

/*
 * Traverse a directory, and chown() when needed.
 */
int
traverse(const char *dir)
{
  register DIR *cur_dir;
  register struct dirent *ent;
  register int len;
  struct stat buf;
  dev_t cur_dev;

  if (chdir(dir))
    return -1;

  /* Change directory, and open it if possible */
  if (!(cur_dir = opendir(".")))
    {
      fprintf(stderr, "Warning: couldn't read directory %s, error %d.\n",
              dir, errno);
      return -1;
    }

  while ((ent = readdir(cur_dir)) != NULL)
    {
      len = NAMELEN(ent);
      /* Skip this file if it's the parent directory, i.e. `..`. */
      if (len == 2 && ent->d_name[0] == '.' && ent->d_name[1] == '.')
        continue;

      if (access(ent->d_name, R_OK) || lstat(ent->d_name, &buf))
        {
          fprintf(stderr, "Warning: couldn't stat %s, error %d.\n",
                  ent->d_name, errno);
          continue;
        }

      /* Only change UID/GID if necessary. */
      if ((buf.st_gid != fgid[buf.st_gid]) ||
          (buf.st_uid != fuid[buf.st_uid]))
        chown(ent->d_name, fuid[buf.st_uid], fgid[buf.st_gid]);

      /* IF it's a directory, and IF it's not a symlink, and IF
         it's not the `.' directory, traverse it! */
      if (!(buf.st_mode & S_IFLNK) && (buf.st_mode & S_IFDIR) &&
          (len != 1 || ent->d_name[0] != '.'))
        {
          cur_dev = buf.st_dev;

          /* Now make sure we don't cross mount points. */
          if (access(".", R_OK) || stat(".", &buf))
            {
              fprintf(stderr, "Warning: couldn't stat ., error %d.\n", errno);
              exit(1);
            }
          if (buf.st_dev == cur_dev)
            {
              if (verbose)
                printf("Entering %s\n", ent->d_name);
              traverse(ent->d_name);
            }
        }
    }
  closedir(cur_dir);

  if (chdir(".."))
    return -1;

  return 0;
}

/*
 * Main routine.
 */
void
main(int argc, char *argv[])
{
  int c, index, errflg = 0;
  char *del, *dir = ".";

  extern char *optarg;

  init();
  while ((c = getopt(argc, argv, "d:g:u:v")) != EOF)
    switch (c)
      {
      case 'd':
        if (!access(optarg, R_OK))
          dir = optarg;
        else
          {
            fprintf(stderr, "%s: No such file or directory\n", optarg);
            errflg++;
          }
        break;
      case 'g':
        if (!(del = strchr(optarg, DELIMITER)))
          errflg++;
        else
          {
            index = atoi(optarg);
            if (errno)
              errflg++;
            else
              fgid[index] = atoi(del + 1);
            if (errno)
              errflg++;
          }
        break;
      case 'u':
        if (!(del = strchr(optarg, DELIMITER)))
          errflg++;
        else
          {
            index = atoi(optarg);
            if (errno)
              errflg++;
            else
              fuid[index] = atoi(del + 1);
            if (errno)
              errflg++;
          }
        break;
      case 'v':
        verbose = 1;
        break;
      }
  if (errflg)
    {
      fprintf(stderr, "usage: bchmod [-g #-#]... [-u #-#]... [-d dir]
[-v]\n");
      exit(1);
    }
  traverse(dir);
}
-------------------------- S N I P ---------------------------------



This archive was generated by hypermail 2.1.2 : Fri Sep 28 2001 - 23:09:14 CDT