home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / fileutils-3.6 / lib / makepath.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-10  |  6.5 KB  |  269 lines

  1. /* makepath.c -- Ensure that a directory path exists.
  2.    Copyright (C) 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and
  19.    Jim Meyering <meyering@cs.utexas.edu>.  */
  20.  
  21. #ifdef __GNUC__
  22. #define alloca __builtin_alloca
  23. #else
  24. #ifdef HAVE_ALLOCA_H
  25. #include <alloca.h>
  26. #else
  27. #ifdef _AIX
  28.  #pragma alloca
  29. #else
  30. char *alloca ();
  31. #endif
  32. #endif
  33. #endif
  34.  
  35. #include <stdio.h>
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #ifdef HAVE_UNISTD_H
  39. #include <unistd.h>
  40. #endif
  41. #if !defined(S_ISDIR) && defined(S_IFDIR)
  42. #define    S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  43. #endif
  44.  
  45. #ifdef STDC_HEADERS
  46. #include <stdlib.h>
  47. #endif
  48.  
  49. #ifdef HAVE_ERRNO_H
  50. #include <errno.h>
  51. #endif
  52.  
  53. #ifndef STDC_HEADERS
  54. extern int errno;
  55. #endif
  56.  
  57. #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
  58. #include <string.h>
  59. #ifndef index
  60. #define index strchr
  61. #endif
  62. #else
  63. #include <strings.h>
  64. #endif
  65.  
  66. #ifdef __MSDOS__
  67. typedef int uid_t;
  68. typedef int gid_t;
  69. #endif
  70.  
  71. void error ();
  72.  
  73. /* Ensure that the directory ARGPATH exists.
  74.    Remove any trailing slashes from ARGPATH before calling this function.
  75.  
  76.    Make any leading directories that don't already exist, with
  77.    permissions PARENT_MODE.
  78.    If the last element of ARGPATH does not exist, create it as
  79.    a new directory with permissions MODE.
  80.    If OWNER and GROUP are non-negative, make them the UID and GID of
  81.    created directories.
  82.    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
  83.    string for printing a message after successfully making a directory,
  84.    with the name of the directory that was just made as an argument.
  85.  
  86.    Return 0 if ARGPATH exists as a directory with the proper
  87.    ownership and permissions when done, otherwise 1.  */
  88.  
  89. int
  90. make_path (argpath, mode, parent_mode, owner, group, verbose_fmt_string)
  91.      char *argpath;
  92.      int mode;
  93.      int parent_mode;
  94.      uid_t owner;
  95.      gid_t group;
  96.      char *verbose_fmt_string;
  97. {
  98.   char *dirpath;        /* A copy we can scribble NULs on.  */
  99.   struct stat stats;
  100.   int retval = 0;
  101.   int oldmask = umask (0);
  102.  
  103.   dirpath = (char *) alloca (strlen (argpath) + 1);
  104.   strcpy (dirpath, argpath);
  105.  
  106.   if (stat (dirpath, &stats))
  107.     {
  108.       char *slash;
  109.       int tmp_mode;        /* Initial perms for leading dirs.  */
  110.       int re_protect;        /* Should leading dirs be unwritable? */
  111.       struct ptr_list
  112.       {
  113.     char *dirname_end;
  114.     struct ptr_list *next;
  115.       };
  116.       struct ptr_list *p, *leading_dirs = NULL;
  117.  
  118.       /* If leading directories shouldn't be writable or executable,
  119.      or should have set[ug]id or sticky bits set and we are setting
  120.      their owners, we need to fix their permissions after making them.  */
  121.       if (((parent_mode & 0300) != 0300)
  122.       || (owner != (uid_t) -1 && group != (gid_t) -1
  123.           && (parent_mode & 07000) != 0))
  124.     {
  125.       tmp_mode = 0700;
  126.       re_protect = 1;
  127.     }
  128.       else
  129.     {
  130.       tmp_mode = parent_mode;
  131.       re_protect = 0;
  132.     }
  133.  
  134.       slash = dirpath;
  135.       while (*slash == '/')
  136.     slash++;
  137.       while ((slash = index (slash, '/')))
  138.     {
  139.       *slash = '\0';
  140.       if (stat (dirpath, &stats))
  141.         {
  142.           if (mkdir (dirpath, tmp_mode))
  143.         {
  144.           error (0, errno, "cannot make directory `%s'", dirpath);
  145.           umask (oldmask);
  146.           return 1;
  147.         }
  148.           else
  149.         {
  150.           if (verbose_fmt_string != NULL)
  151.             error (0, 0, verbose_fmt_string, dirpath);
  152.  
  153.           if (owner != (uid_t) -1 && group != (gid_t) -1
  154.               && chown (dirpath, owner, group)
  155. #if defined(AFS) && defined (EPERM)
  156.               && errno != EPERM
  157. #endif
  158.               )
  159.             {
  160.               error (0, errno, "%s", dirpath);
  161.               retval = 1;
  162.             }
  163.           if (re_protect)
  164.             {
  165.               struct ptr_list *new = (struct ptr_list *)
  166.             alloca (sizeof (struct ptr_list));
  167.               new->dirname_end = slash;
  168.               new->next = leading_dirs;
  169.               leading_dirs = new;
  170.             }
  171.         }
  172.         }
  173.       else if (!S_ISDIR (stats.st_mode))
  174.         {
  175.           error (0, 0, "`%s' exists but is not a directory", dirpath);
  176.           umask (oldmask);
  177.           return 1;
  178.         }
  179.  
  180.       *slash++ = '/';
  181.  
  182.       /* Avoid unnecessary calls to `stat' when given
  183.          pathnames containing multiple adjacent slashes.  */
  184.       while (*slash == '/')
  185.         slash++;
  186.     }
  187.  
  188.       /* We're done making leading directories.
  189.      Make the final component of the path.  */
  190.  
  191.       if (mkdir (dirpath, mode))
  192.     {
  193.       error (0, errno, "cannot make directory `%s'", dirpath);
  194.       umask (oldmask);
  195.       return 1;
  196.     }
  197.       if (verbose_fmt_string != NULL)
  198.     error (0, 0, verbose_fmt_string, dirpath);
  199.  
  200.       if (owner != (uid_t) -1 && group != (gid_t) -1)
  201.     {
  202.       if (chown (dirpath, owner, group)
  203. #ifdef AFS
  204.           && errno != EPERM
  205. #endif
  206.           )
  207.         {
  208.           error (0, errno, "%s", dirpath);
  209.           retval = 1;
  210.         }
  211.       /* chown may have turned off some permission bits we wanted.  */
  212.       if ((mode & 07000) != 0 && chmod (dirpath, mode))
  213.         {
  214.           error (0, errno, "%s", dirpath);
  215.           retval = 1;
  216.         }
  217.     }
  218.  
  219.       /* If the mode for leading directories didn't include owner "wx"
  220.      privileges, we have to reset their protections to the correct
  221.      value.  */
  222.       for (p = leading_dirs; p != NULL; p = p->next)
  223.     {
  224.       *(p->dirname_end) = '\0';
  225.       if (chmod (dirpath, parent_mode))
  226.         {
  227.           error (0, errno, "%s", dirpath);
  228.           retval = 1;
  229.         }
  230.     }
  231.     }
  232.   else
  233.     {
  234.       /* We get here if the entire path already exists.  */
  235.  
  236.       if (!S_ISDIR (stats.st_mode))
  237.     {
  238.       error (0, 0, "`%s' exists but is not a directory", dirpath);
  239.       umask (oldmask);
  240.       return 1;
  241.     }
  242.  
  243.       /* chown must precede chmod because on some systems,
  244.      chown clears the set[ug]id bits for non-superusers,
  245.      resulting in incorrect permissions.
  246.      On System V, users can give away files with chown and then not
  247.      be able to chmod them.  So don't give files away.  */
  248.  
  249.       if (owner != (uid_t) -1 && group != (gid_t) -1
  250.       && chown (dirpath, owner, group)
  251. #ifdef AFS
  252.       && errno != EPERM
  253. #endif
  254.       )
  255.     {
  256.       error (0, errno, "%s", dirpath);
  257.       retval = 1;
  258.     }
  259.       if (chmod (dirpath, mode))
  260.     {
  261.       error (0, errno, "%s", dirpath);
  262.       retval = 1;
  263.     }
  264.     }
  265.  
  266.   umask (oldmask);
  267.   return retval;
  268. }
  269.