*** empty log message ***
authorJim Meyering <jim@meyering.net>
Sat, 9 Dec 2000 14:47:06 +0000 (14:47 +0000)
committerJim Meyering <jim@meyering.net>
Sat, 9 Dec 2000 14:47:06 +0000 (14:47 +0000)
src/chown-core.c [new file with mode: 0644]

diff --git a/src/chown-core.c b/src/chown-core.c
new file mode 100644 (file)
index 0000000..73568c2
--- /dev/null
@@ -0,0 +1,252 @@
+/* chown-core.c -- core functions for changing ownership.
+   Copyright (C) 2000 Free Software Foundation.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Extracted from chown.c/chgrp.c and librarified by Jim Meyering.  */
+
+#include <config.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "system.h"
+#include "error.h"
+#include "lchown.h"
+#include "quote.h"
+#include "savedir.h"
+#include "chown-core.h"
+
+void
+chopt_init (struct Chown_option *chopt)
+{
+  chopt->verbosity = V_off;
+  chopt->change_symlinks = 1;
+  chopt->recurse = 0;
+  chopt->force_silent = 0;
+  chopt->user_name = 0;
+  chopt->group_name = 0;
+}
+
+/* Tell the user how/if the user and group of FILE have been changed.
+   If USER is NULL, give the group-oriented messages.
+   CHANGED describes what (if anything) has happened. */
+
+static void
+describe_change (const char *file, enum Change_status changed,
+                char const *user, char const *group)
+{
+  const char *fmt;
+
+  if (changed == CH_NOT_APPLIED)
+    {
+      printf (_("neither symbolic link %s nor referent has been changed\n"),
+             quote (file));
+      return;
+    }
+
+  if (user == NULL)
+    {
+      switch (changed)
+       {
+       case CH_SUCCEEDED:
+         fmt = _("group of %s changed to %s\n");
+         break;
+       case CH_FAILED:
+         fmt = _("failed to change group of %s to %s\n");
+         break;
+       case CH_NO_CHANGE_REQUESTED:
+         fmt = _("group of %s retained as %s\n");
+         break;
+       default:
+         abort ();
+       }
+      printf (fmt, quote (file), group);
+    }
+  else
+    {
+      switch (changed)
+       {
+       case CH_SUCCEEDED:
+         fmt = _("ownership of %s changed to ");
+         break;
+       case CH_FAILED:
+         fmt = _("failed to change ownership of %s to ");
+         break;
+       case CH_NO_CHANGE_REQUESTED:
+         fmt = _("ownership of %s retained as ");
+         break;
+       default:
+         abort ();
+       }
+      printf (fmt, file);
+      if (group)
+       printf ("%s.%s\n", user, group);
+      else
+       printf ("%s\n", user);
+    }
+}
+
+/* Recursively change the ownership of the files in directory DIR
+   to UID USER and GID GROUP, according to the options specified by CHOPT.
+   STATP points to the results of lstat on DIR.
+   Return 0 if successful, 1 if errors occurred. */
+
+static int
+change_dir_owner (const char *dir, uid_t user, gid_t group,
+                 uid_t old_user, gid_t old_group,
+                 const struct stat *statp,
+                 struct Chown_option const *chopt)
+{
+  char *name_space, *namep;
+  char *path;                  /* Full path of each entry to process. */
+  unsigned dirlength;          /* Length of `dir' and '\0'. */
+  unsigned filelength;         /* Length of each pathname to process. */
+  unsigned pathlength;         /* Bytes allocated for `path'. */
+  int errors = 0;
+
+  name_space = savedir (dir, statp->st_size);
+  if (name_space == NULL)
+    {
+      if (chopt->force_silent == 0)
+       error (0, errno, "%s", quote (dir));
+      return 1;
+    }
+
+  dirlength = strlen (dir) + 1;        /* + 1 is for the trailing '/'. */
+  pathlength = dirlength + 1;
+  /* Give `path' a dummy value; it will be reallocated before first use. */
+  path = xmalloc (pathlength);
+  strcpy (path, dir);
+  path[dirlength - 1] = '/';
+
+  for (namep = name_space; *namep; namep += filelength - dirlength)
+    {
+      filelength = dirlength + strlen (namep) + 1;
+      if (filelength > pathlength)
+       {
+         pathlength = filelength * 2;
+         path = xrealloc (path, pathlength);
+       }
+      strcpy (path + dirlength, namep);
+      errors |= change_file_owner (0, path, user, group, old_user, old_group,
+                                  chopt);
+    }
+  free (path);
+  free (name_space);
+  return errors;
+}
+
+/* Change the ownership of FILE to UID USER and GID GROUP
+   provided it presently has UID OLDUSER and GID OLDGROUP.
+   Honor the options specified by CHOPT.
+   If FILE is a directory and -R is given, recurse.
+   Return 0 if successful, 1 if errors occurred. */
+
+int
+change_file_owner (int cmdline_arg, const char *file, uid_t user, gid_t group,
+                  uid_t old_user, gid_t old_group,
+                  struct Chown_option const *chopt)
+{
+  struct stat file_stats;
+  uid_t newuser;
+  gid_t newgroup;
+  int errors = 0;
+
+  if (lstat (file, &file_stats))
+    {
+      if (chopt->force_silent == 0)
+       error (0, errno, _("getting attributes of %s"), quote (file));
+      return 1;
+    }
+
+  if ((old_user == (uid_t) -1  || file_stats.st_uid == old_user) &&
+      (old_group == (gid_t) -1 || file_stats.st_gid == old_group))
+    {
+      newuser = user == (uid_t) -1 ? file_stats.st_uid : user;
+      newgroup = group == (gid_t) -1 ? file_stats.st_gid : group;
+      if (newuser != file_stats.st_uid || newgroup != file_stats.st_gid)
+       {
+         int fail;
+         int symlink_changed = 1;
+         int saved_errno;
+
+         if (S_ISLNK (file_stats.st_mode) && chopt->change_symlinks)
+           {
+             fail = lchown (file, newuser, newgroup);
+
+             /* Ignore the failure if it's due to lack of support (ENOSYS)
+                and this is not a command line argument.  */
+             if (!cmdline_arg && fail && errno == ENOSYS)
+               {
+                 fail = 0;
+                 symlink_changed = 0;
+               }
+           }
+         else
+           {
+             fail = chown (file, newuser, newgroup);
+           }
+         saved_errno = errno;
+
+         if (chopt->verbosity == V_high
+             || (chopt->verbosity == V_changes_only && !fail))
+           {
+             enum Change_status ch_status = (! symlink_changed
+                                             ? CH_NOT_APPLIED
+                                             : (fail
+                                                ? CH_FAILED : CH_SUCCEEDED));
+             describe_change (file, ch_status,
+                              chopt->user_name, chopt->group_name);
+           }
+
+         if (fail)
+           {
+             if (chopt->force_silent == 0)
+               error (0, saved_errno, _("changing ownership of %s"),
+                      quote (file));
+             errors = 1;
+           }
+         else
+           {
+             /* The change succeeded.  On some systems, the chown function
+                resets the `special' permission bits.  When run by a
+                `privileged' user, this program must ensure that at least
+                the set-uid and set-group ones are still set.  */
+             if (file_stats.st_mode & ~S_IRWXUGO
+                 /* If this is a symlink and we changed *it*, then skip it.  */
+                 && ! (S_ISLNK (file_stats.st_mode) && chopt->change_symlinks))
+               {
+                 if (chmod (file, file_stats.st_mode))
+                   {
+                     error (0, saved_errno,
+                            _("unable to restore permissions of %s"),
+                            quote (file));
+                     fail = 1;
+                   }
+               }
+           }
+       }
+      else if (chopt->verbosity == V_high)
+       {
+         describe_change (file, CH_NO_CHANGE_REQUESTED,
+                          chopt->user_name, chopt->group_name);
+       }
+    }
+
+  if (chopt->recurse && S_ISDIR (file_stats.st_mode))
+    errors |= change_dir_owner (file, user, group,
+                               old_user, old_group, &file_stats, chopt);
+  return errors;
+}