home *** CD-ROM | disk | FTP | other *** search
- /* mvdir -- rename directory
- Copyright (C) 1990 Free Software Foundation, Inc.
-
- 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* Helper program for GNU mv on machines that lack the rename system call.
-
- Usage: mvdir from to
-
- FROM must be an existing directory.
- TO must not exist, but its parent must exist.
-
- Must be setuid root.
-
- Ian Dall (ian@sibyl.eleceng.ua.oz.au)
- and David MacKenzie (djm@ai.mit.edu) */
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <signal.h>
- #include "system.h"
-
- #ifdef STDC_HEADERS
- #include <stdlib.h>
- #include <errno.h>
- #else
- char *malloc ();
-
- extern int errno;
- #endif
-
- #ifndef HIPRI
- #define HIPRI -10
- #endif
-
- #ifdef DEBUG
- #define link(FROM, TO) (printf("Linking %s to %s\n", FROM, TO), 0)
- #define unlink(FILE) (printf("Unlinking %s\n", FILE), 0)
- #endif
-
- /* The name this program was run with. */
- char *program_name;
-
- char *basename ();
- char *fullpath ();
- char *parent_dir ();
- char *xmalloc ();
- void error ();
- void strip_trailing_slashes ();
-
- void
- main (argc, argv)
- int argc;
- char **argv;
- {
- char *from, *from_parent, *from_base;
- char *to, *to_parent, *to_parent_path;
- struct stat from_stats, to_stats;
- char *slash, temp;
- int i;
-
- program_name = argv[0];
- if (argc != 3)
- {
- fprintf (stderr, "Usage: %s existing-dir new-dir\n", program_name);
- exit (2);
- }
- from = argv[1];
- to = argv[2];
- strip_trailing_slashes (from);
- strip_trailing_slashes (to);
- from_parent = parent_dir (from);
- to_parent = parent_dir (to);
-
- /* Make sure `from' is not "." or "..". */
- from_base = basename (from);
- if (!strcmp (from_base, ".") || !strcmp (from_base, ".."))
- error (1, 0, "cannot rename `.' or `..'");
-
- /* Even with an effective uid of root, link fails if the target exists.
- That is what we want, so don't unlink `to' first.
- However, we do need to check that the directories that link and unlink
- will modify exist and are writable by the user. */
-
- if (stat (from, &from_stats))
- error (1, errno, "%s", from);
- if ((from_stats.st_mode & S_IFMT) != S_IFDIR)
- error (1, 0, "`%s' is not a directory", from);
- if (access (from_parent, W_OK))
- error (1, errno, "cannot write to `%s'", from_parent);
- if (access (to_parent, W_OK))
- error (1, errno, "cannot write to `%s'", to_parent);
-
- /* To prevent disconnecting the tree rooted at `from' from its parent,
- quit if any of the directories in `to' are the same (dev and ino)
- as the directory `from'. */
-
- slash = to_parent_path = fullpath (to_parent);
- while (*slash)
- {
- slash = index (slash, '/');
- if (slash)
- {
- ++slash;
- temp = *slash;
- *slash = '\0';
- if (stat (to_parent_path, &to_stats))
- error (1, errno, "%s", to_parent_path);
- *slash = temp;
- }
- else
- {
- /* Last element of path. */
- slash = "";
- if (stat (to_parent_path, &to_stats))
- error (1, errno, "%s", to_parent_path);
- }
-
- if (to_stats.st_dev == from_stats.st_dev
- && to_stats.st_ino == from_stats.st_ino)
- error (1, 0, "`%s' is an ancestor of `%s'", from, to);
- }
-
- /* We can't make the renaming atomic, but we do our best. */
- for (i = NSIG; i > 0; i--)
- if (i != SIGKILL)
- signal (i, SIG_IGN);
- setuid (0); /* Make real uid 0 so it is harder to kill. */
- nice (HIPRI - nice (0)); /* Raise priority. */
-
- if (link (from, to))
- error (1, errno, "cannot link `%s' to `%s'", from, to);
- if (unlink (from))
- error (1, errno, "cannot unlink `%s'", from);
-
- /* Replace the directory's `..' entry. It used to be a link to
- the parent of `from'; make it a link to the parent of `to' instead. */
- i = strlen (to);
- slash = xmalloc (i + 4);
- strcpy (slash, to);
- strcpy (slash + i, "/..");
- if (unlink (slash) && errno != ENOENT)
- error (1, errno, "cannot unlink `%s'", slash);
- if (link (to_parent, slash))
- error (1, errno, "cannot link `%s' to `%s'", to_parent, slash);
-
- exit (0);
- }
-
- /* Return the name of the directory containing PATH. */
-
- char *
- parent_dir (path)
- char *path;
- {
- char *dir;
- char *base;
- int length;
-
- base = rindex (path, '/');
- if (base == NULL)
- return ".";
-
- if (base > path)
- base--;
- length = base - path + 1;
- dir = xmalloc (length + 1);
- strncpy (dir, path, length);
- dir[length] = '\0';
- return dir;
- }
-
- /* Return NAME with any leading path stripped off. */
-
- char *
- basename (name)
- char *name;
- {
- char *base;
-
- base = rindex (name, '/');
- return base ? base + 1 : name;
- }
-
- /* Return the full pathname (from /) of the directory DIR,
- as static data. */
-
- char *
- fullpath (dir)
- char *dir;
- {
- char wd[PATH_MAX + 2];
- static char path[PATH_MAX + 2];
-
- if (getwd (wd) == NULL)
- error (1, errno, "cannot get current directory");
- if (chdir (dir))
- error (1, errno, "%s", dir);
- if (getwd (path) == NULL)
- error (1, errno, "cannot get current directory");
- if (chdir (wd))
- error (1, errno, "%s", wd);
-
- return path;
- }
-
- /* Allocate N bytes of memory dynamically, with error checking. */
-
- char *
- xmalloc (n)
- unsigned n;
- {
- char *p;
-
- p = malloc (n);
- if (p == 0)
- error (1, 0, "virtual memory exhausted");
- return p;
- }
-
- /* Remove any trailing slashes from STR. */
-
- void
- strip_trailing_slashes (str)
- char *str;
- {
- int last = strlen (str) - 1;
-
- while (last > 0 && str[last] == '/')
- str[last--] = '\0';
- }
-