]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - contrib/cpio/src/makepath.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.git] / contrib / cpio / src / makepath.c
1 /* makepath.c -- Ensure that a directory path exists.
2    Copyright (C) 1990, 2006 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
15    License along with this program; if not, write to the Free
16    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301 USA.  */
18
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and
20    Jim Meyering <meyering@cs.utexas.edu>.  */
21
22 /* This copy of makepath is almost like the fileutils one, but has
23    changes for HPUX CDF's.  Maybe the 2 versions of makepath can
24    come together again in the future.  */
25
26 #include <system.h>
27 #include <paxlib.h>
28
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 /* Ensure that the directory ARGPATH exists.
34    Remove any trailing slashes from ARGPATH before calling this function.
35
36    Make any leading directories that don't already exist, with
37    permissions PARENT_MODE.
38    If the last element of ARGPATH does not exist, create it as
39    a new directory with permissions MODE.
40    If OWNER and GROUP are non-negative, make them the UID and GID of
41    created directories.
42    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
43    string for printing a message after successfully making a directory,
44    with the name of the directory that was just made as an argument.
45
46    Return 0 if ARGPATH exists as a directory with the proper
47    ownership and permissions when done, otherwise 1.  */
48
49 int
50 make_path (char *argpath,
51            int mode,
52            int parent_mode,
53            uid_t owner,
54            gid_t group,
55            char *verbose_fmt_string)
56 {
57   char *dirpath;                /* A copy we can scribble NULs on.  */
58   struct stat stats;
59   int retval = 0;
60   int oldmask = umask (0);
61   dirpath = alloca (strlen (argpath) + 1);
62   strcpy (dirpath, argpath);
63
64   if (stat (dirpath, &stats))
65     {
66       char *slash;
67       int tmp_mode;             /* Initial perms for leading dirs.  */
68       int re_protect;           /* Should leading dirs be unwritable? */
69       struct ptr_list
70       {
71         char *dirname_end;
72         struct ptr_list *next;
73       };
74       struct ptr_list *p, *leading_dirs = NULL;
75
76       /* If leading directories shouldn't be writable or executable,
77          or should have set[ug]id or sticky bits set and we are setting
78          their owners, we need to fix their permissions after making them.  */
79       if (((parent_mode & 0300) != 0300)
80           || (owner != (uid_t) -1 && group != (gid_t) -1
81               && (parent_mode & 07000) != 0))
82         {
83           tmp_mode = 0700;
84           re_protect = 1;
85         }
86       else
87         {
88           tmp_mode = parent_mode;
89           re_protect = 0;
90         }
91
92       slash = dirpath;
93       while (*slash == '/')
94         slash++;
95       while ((slash = strchr (slash, '/')))
96         {
97 #ifdef HPUX_CDF
98           int   iscdf;
99           iscdf = 0;
100 #endif
101           *slash = '\0';
102           if (stat (dirpath, &stats))
103             {
104 #ifdef HPUX_CDF
105               /* If this component of the pathname ends in `+' and is
106                  followed by 2 `/'s, then this is a CDF.  We remove the
107                  `+' from the name and create the directory.  Later
108                  we will "hide" the directory.  */
109               if ( (*(slash +1) == '/') && (*(slash -1) == '+') )
110                 { 
111                   iscdf = 1;
112                   *(slash -1) = '\0';
113                 }
114 #endif
115               if (mkdir (dirpath, tmp_mode))
116                 {
117                   error (0, errno, _("cannot make directory `%s'"), dirpath);
118                   umask (oldmask);
119                   return 1;
120                 }
121               else
122                 {
123                   if (verbose_fmt_string != NULL)
124                     error (0, 0, verbose_fmt_string, dirpath);
125
126                   if (owner != (uid_t) -1 && group != (gid_t) -1
127                       && chown (dirpath, owner, group)
128 #ifdef AFS
129                       && errno != EPERM
130 #endif
131                       )
132                     {
133                       chown_error_details (dirpath, owner, group);
134                       retval = 1;
135                     }
136                   if (re_protect)
137                     {
138                       struct ptr_list *new = (struct ptr_list *)
139                         alloca (sizeof (struct ptr_list));
140                       new->dirname_end = slash;
141                       new->next = leading_dirs;
142                       leading_dirs = new;
143                     }
144 #ifdef HPUX_CDF
145                   if (iscdf)
146                     {
147                       /*  If this is a CDF, "hide" the directory by setting
148                           its hidden/setuid bit.  Also add the `+' back to
149                           its name (since once it's "hidden" we must refer
150                           to as `name+' instead of `name').  */
151                       chmod (dirpath, 04700);
152                       *(slash - 1) = '+';
153                     }
154 #endif
155                 }
156             }
157           else if (!S_ISDIR (stats.st_mode))
158             {
159               error (0, 0, _("`%s' exists but is not a directory"), dirpath);
160               umask (oldmask);
161               return 1;
162             }
163
164           *slash++ = '/';
165
166           /* Avoid unnecessary calls to `stat' when given
167              pathnames containing multiple adjacent slashes.  */
168           while (*slash == '/')
169             slash++;
170         }
171
172       /* We're done making leading directories.
173          Make the final component of the path. */
174
175       if (mkdir (dirpath, mode))
176         {
177           /* In some cases, if the final component in dirpath was `.' then we 
178              just got an EEXIST error from that last mkdir().  If that's
179              the case, ignore it.  */
180           if ( (errno != EEXIST) ||
181                (stat (dirpath, &stats) != 0) ||
182                (!S_ISDIR (stats.st_mode) ) )
183             {
184               error (0, errno, _("cannot make directory `%s'"), dirpath);
185               umask (oldmask);
186               return 1;
187             }
188         }
189       if (verbose_fmt_string != NULL)
190         error (0, 0, verbose_fmt_string, dirpath);
191
192       if (owner != (uid_t) -1 && group != (gid_t) -1)
193         {
194           if (chown (dirpath, owner, group)
195 #ifdef AFS
196               && errno != EPERM
197 #endif
198               )
199             {
200               chown_error_details (dirpath, owner, group);
201               retval = 1;
202             }
203         }
204       /* chown may have turned off some permission bits we wanted.  */
205       if ((mode & 07000) != 0 && chmod (dirpath, mode))
206         {
207           chmod_error_details (dirpath, mode);
208           retval = 1;
209         }
210
211       /* If the mode for leading directories didn't include owner "wx"
212          privileges, we have to reset their protections to the correct
213          value.  */
214       for (p = leading_dirs; p != NULL; p = p->next)
215         {
216           *p->dirname_end = '\0';
217 #if 0
218           /* cpio always calls make_path with parent mode 0700, so
219              we don't have to do this.  If we ever do have to do this,
220              we have to stat the directory first to get the setuid
221              bit so we don't break HP CDF's.  */
222           if (chmod (dirpath, parent_mode))
223             {
224               chmod_error_details (dirpath, parent_mode);
225               retval = 1;
226             }
227 #endif
228
229         }
230     }
231   else
232     {
233       /* We get here if the entire path already exists.  */
234
235       if (!S_ISDIR (stats.st_mode))
236         {
237           error (0, 0, _("`%s' exists but is not a directory"), dirpath);
238           umask (oldmask);
239           return 1;
240         }
241
242       /* chown must precede chmod because on some systems,
243          chown clears the set[ug]id bits for non-superusers,
244          resulting in incorrect permissions.
245          On System V, users can give away files with chown and then not
246          be able to chmod them.  So don't give files away.  */
247
248       if (owner != (uid_t) -1 && group != (gid_t) -1
249           && chown (dirpath, owner, group)
250 #ifdef AFS
251           && errno != EPERM
252 #endif
253           )
254         {
255           chown_error_details (dirpath, owner, group);
256           retval = 1;
257         }
258       if (chmod (dirpath, mode))
259         {
260           chmod_error_details (dirpath, mode);
261           retval = 1;
262         }
263     }
264
265   umask (oldmask);
266   return retval;
267 }