]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/makepath.c
kes When applying a storage override, release all previous storage
[bacula/bacula] / bacula / src / findlib / makepath.c
1 /* makepath.c -- Ensure that a directory path exists.
2
3    Copyright (C) 1990, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Jim Meyering.  */
20 /*
21  *   Modified by Kern Sibbald for use in Bacula, December 2000
22  *
23  *   Version $Id$
24  */
25
26 #include "bacula.h"
27 #include "jcr.h"
28 #include "save-cwd.h"
29
30
31 #if !defined(S_ISDIR) && defined(S_IFDIR)
32 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
33 #endif
34
35 #ifndef S_IRWXUGO
36 # define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO)
37 #endif
38
39
40 #ifndef S_ISUID
41 # define S_ISUID 04000
42 #endif
43 #ifndef S_ISGID
44 # define S_ISGID 02000
45 #endif
46 #ifndef S_ISVTX
47 # define S_ISVTX 01000
48 #endif
49 #ifndef S_IRUSR
50 # define S_IRUSR 0200
51 #endif
52 #ifndef S_IWUSR
53 # define S_IWUSR 0200
54 #endif
55 #ifndef S_IXUSR
56 # define S_IXUSR 0100
57 #endif
58
59 #ifndef S_IRWXU
60 # define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
61 #endif
62
63 #define WX_USR (S_IWUSR | S_IXUSR)
64
65 #define quote(path) path
66
67 extern void strip_trailing_slashes();
68
69 static int
70 cleanup(struct saved_cwd *cwd)
71 {
72    if (cwd->do_chdir) {
73       int _fail = restore_cwd(cwd, NULL, NULL);
74       free_cwd(cwd);
75       if (_fail) {
76          return 1;
77       }
78    }
79    return 0;
80 }
81
82
83 /* Attempt to create directory DIR (aka DIRPATH) with the specified MODE.
84    If CREATED_DIR_P is non-NULL, set *CREATED_DIR_P to non-zero if this
85    function creates DIR and to zero otherwise.  Give a diagnostic and
86    return non-zero if DIR cannot be created or cannot be determined to
87    exist already.  Use DIRPATH in any diagnostic, not DIR.
88    Note that if DIR already exists, this function will return zero
89    (indicating success) and will set *CREATED_DIR_P to zero.  */
90
91 static int
92 make_dir(JCR *jcr, const char *dir, const char *dirpath, mode_t mode, int *created_dir_p)
93 {
94   int fail = 0;
95   int created_dir;
96   int save_errno;
97
98   Dmsg2(300, "make_dir mode=%o dir=%s\n", mode, dir);
99   created_dir = (mkdir(dir, mode) == 0);
100   save_errno = errno;
101
102   if (!created_dir) {
103       struct stat stats;
104
105       /* The mkdir and stat calls below may appear to be reversed.
106          They are not.  It is important to call mkdir first and then to
107          call stat (to distinguish the three cases) only if mkdir fails.
108          The alternative to this approach is to `stat' each directory,
109          then to call mkdir if it doesn't exist.  But if some other process
110          were to create the directory between the stat & mkdir, the mkdir
111          would fail with EEXIST.  */
112
113       if (stat(dir, &stats)) {
114           berrno be;
115           be.set_errno(save_errno);
116           Jmsg(jcr, M_ERROR, 0, _("Cannot create directory %s: ERR=%s\n"),
117                   dirpath, be.bstrerror());
118           fail = 1;
119       } else if (!S_ISDIR(stats.st_mode)) {
120           Jmsg(jcr, M_ERROR, 0, _("%s exists but is not a directory\n"), quote(dirpath));
121           fail = 1;
122       } else {
123           /* DIR (aka DIRPATH) already exists and is a directory. */
124       }
125   }
126
127   if (created_dir_p) {
128      *created_dir_p = created_dir;
129   }
130
131   return fail;
132 }
133
134 /* return non-zero if path is absolute or zero if relative. */
135 int
136 isAbsolute(const char *path)
137 {
138 #if defined(HAVE_WIN32)
139     return path[1] == ':' || IsPathSeparator(*path);     /* drivespec:/blah is absolute */
140 #else
141     return IsPathSeparator(*path);
142 #endif
143 }
144
145 /* Ensure that the directory ARGPATH exists.
146    Remove any trailing slashes from ARGPATH before calling this function.
147
148    Create any leading directories that don't already exist, with
149    permissions PARENT_MODE.
150
151    If the last element of ARGPATH does not exist, create it as
152    a new directory with permissions MODE.
153
154    If OWNER and GROUP are non-negative, use them to set the UID and GID of
155    any created directories.
156
157    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
158    string for printing a message after successfully making a directory,
159    with the name of the directory that was just made as an argument.
160
161    If PRESERVE_EXISTING is non-zero and ARGPATH is an existing directory,
162    then do not attempt to set its permissions and ownership.
163
164    Return 0 if ARGPATH exists as a directory with the proper
165    ownership and permissions when done, otherwise 1.  */
166
167 int
168 make_path(
169            JCR *jcr,
170            const char *argpath,
171            int mode,
172            int parent_mode,
173            uid_t owner,
174            gid_t group,
175            int preserve_existing,
176            char *verbose_fmt_string)
177 {
178   struct stat stats;
179   int retval = 0;
180
181   if (stat(argpath, &stats)) {
182       char *slash;
183       int tmp_mode;              /* Initial perms for leading dirs.  */
184       int re_protect;            /* Should leading dirs be unwritable? */
185       struct ptr_list {
186         char *dirname_end;
187         struct ptr_list *next;
188       };
189       struct ptr_list *p, *leading_dirs = NULL;
190       struct saved_cwd cwd;
191       char *basename_dir;
192       char *dirpath;
193
194       /* Temporarily relax umask in case it's overly restrictive.  */
195       mode_t oldmask = umask(0);
196
197       /* Make a copy of ARGPATH that we can scribble NULs on.  */
198       dirpath = (char *)alloca(strlen(argpath) + 1);
199       strcpy (dirpath, argpath);
200       strip_trailing_slashes(dirpath);
201
202       /* If leading directories shouldn't be writable or executable,
203          or should have set[ug]id or sticky bits set and we are setting
204          their owners, we need to fix their permissions after making them.  */
205       if (((parent_mode & WX_USR) != WX_USR)
206           || ((owner != (uid_t)-1 || group != (gid_t)-1)
207               && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0)) {
208          tmp_mode = S_IRWXU;
209          re_protect = 1;
210       } else {
211          tmp_mode = parent_mode;
212          re_protect = 0;
213       }
214
215 #if defined(HAVE_WIN32)
216       /* chdir can fail if permissions are sufficiently restricted since I don't think
217          backup/restore security rights affect ChangeWorkingDirectory */
218       cwd.do_chdir = 0;
219
220       /* Validate drive letter */
221       if (dirpath[1] == ':') {
222          char drive[4] = "X:\\";
223
224          drive[0] = dirpath[0];
225
226          UINT drive_type = GetDriveType(drive);
227
228          if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) {
229             Jmsg(jcr, M_ERROR, 0, _("%c: is not a valid drive\n"), dirpath[0]);
230             return 1;
231          }
232
233          if (dirpath[2] == '\0') {
234             return 0;
235          }
236
237          slash = &dirpath[3];
238       } else {
239          slash = dirpath;
240       }
241 #else
242       /* If we can record the current working directory, we may be able
243          to do the chdir optimization.  */
244       cwd.do_chdir = !save_cwd(&cwd);
245
246       slash = dirpath;
247 #endif
248
249       /* If we've saved the cwd and DIRPATH is an absolute pathname,
250          we must chdir to `/' in order to enable the chdir optimization.
251          So if chdir ("/") fails, turn off the optimization.  */
252       if (cwd.do_chdir && isAbsolute(dirpath) && (chdir("/") < 0)) {
253          cwd.do_chdir = 0;
254       }
255
256       /* Skip over leading slashes.  */
257       while (IsPathSeparator(*slash))
258          slash++;
259
260       for ( ; ; ) {
261           int newly_created_dir;
262           int fail;
263
264           /* slash points to the leftmost unprocessed component of dirpath.  */
265           basename_dir = slash;
266           slash = first_path_separator(slash);
267           if (slash == NULL) {
268              break;
269           }
270
271           /* If we're *not* doing chdir before each mkdir, then we have to refer
272              to the target using the full (multi-component) directory name.  */
273           if (!cwd.do_chdir) {
274              basename_dir = dirpath;
275           }
276
277           *slash = '\0';
278           fail = make_dir(jcr, basename_dir, dirpath, tmp_mode, &newly_created_dir);
279           if (fail) {
280               umask(oldmask);
281               cleanup(&cwd);
282               return 1;
283           }
284
285           if (newly_created_dir) {
286               Dmsg0(300, "newly_created_dir\n");
287
288               if ((owner != (uid_t)-1 || group != (gid_t)-1)
289                   && chown(basename_dir, owner, group)
290 #if defined(AFS) && defined (EPERM)
291                   && errno != EPERM
292 #endif
293                   ) {
294                  /* Note, if we are restoring as NON-root, this may not be fatal */
295                  berrno be;
296                  Jmsg(jcr, M_ERROR, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
297                       quote(dirpath), be.bstrerror());
298               }
299               Dmsg0(300, "Chown done.\n");
300
301               if (re_protect) {
302                  struct ptr_list *pnew = (struct ptr_list *)
303                     alloca(sizeof (struct ptr_list));
304                  pnew->dirname_end = slash;
305                  pnew->next = leading_dirs;
306                  leading_dirs = pnew;
307                  Dmsg0(300, "re_protect\n");
308               }
309           }
310
311           /* If we were able to save the initial working directory,
312              then we can use chdir to change into each directory before
313              creating an entry in that directory.  This avoids making
314              stat and mkdir process O(n^2) file name components.  */
315           if (cwd.do_chdir && chdir(basename_dir) < 0) {
316               berrno be;
317               Jmsg(jcr, M_ERROR, 0, _("Cannot chdir to directory, %s: ERR=%s\n"),
318                      quote(dirpath), be.bstrerror());
319               umask(oldmask);
320               cleanup(&cwd);
321               return 1;
322           }
323
324           *slash++ = '/';
325
326           /* Avoid unnecessary calls to `stat' when given
327              pathnames containing multiple adjacent slashes.  */
328          while (IsPathSeparator(*slash))
329             slash++;
330       } /* end while (1) */
331
332       if (!cwd.do_chdir) {
333          basename_dir = dirpath;
334       }
335
336       /* We're done making leading directories.
337          Create the final component of the path.  */
338
339       Dmsg1(300, "Create final component. mode=%o\n", mode);
340       if (make_dir(jcr, basename_dir, dirpath, mode, NULL)) {
341           umask(oldmask);
342           cleanup(&cwd);
343           return 1;
344       }
345
346       /* Done creating directories.  Restore original umask.  */
347       umask(oldmask);
348
349       if (owner != (uid_t)-1 || group != (gid_t)-1) {
350           if (chown(basename_dir, owner, group)
351 #ifdef AFS
352               && errno != EPERM
353 #endif
354               )
355             {
356               berrno be;
357               Jmsg(jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
358                      quote(dirpath), be.bstrerror());
359             }
360       }
361
362       /* The above chown may have turned off some permission bits in MODE.
363          Another reason we may have to use chmod here is that mkdir(2) is
364          required to honor only the file permission bits.  In particular,
365          it need not honor the `special' bits, so if MODE includes any
366          special bits, set them here.  */
367       if (mode & ~S_IRWXUGO) {
368          Dmsg1(300, "Final chmod mode=%o\n", mode);
369       }
370       if ((mode & ~S_IRWXUGO) && chmod(basename_dir, mode)) {
371           berrno be;
372           Jmsg(jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
373              quote(dirpath), be.bstrerror());
374       }
375
376      if (cleanup(&cwd)) {
377         return 1;
378      }
379
380       /* If the mode for leading directories didn't include owner "wx"
381          privileges, we have to reset their protections to the correct
382          value.  */
383       for (p = leading_dirs; p != NULL; p = p->next) {
384           *(p->dirname_end) = '\0';
385           Dmsg2(300, "Reset parent mode=%o dir=%s\n", parent_mode, dirpath);
386           if (chmod(dirpath, parent_mode)) {
387               berrno be;
388               Jmsg(jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
389                      quote(dirpath), be.bstrerror());
390           }
391       }
392   } else {
393       /* We get here if the entire path already exists.  */
394
395       const char *dirpath = argpath;
396
397       if (!S_ISDIR(stats.st_mode)) {
398           Jmsg(jcr, M_ERROR, 0, _("%s exists but is not a directory\n"), quote(dirpath));
399           return 1;
400       }
401
402       if (!preserve_existing) {
403           Dmsg0(300, "Do not preserve existing.\n");
404           /* chown must precede chmod because on some systems,
405              chown clears the set[ug]id bits for non-superusers,
406              resulting in incorrect permissions.
407              On System V, users can give away files with chown and then not
408              be able to chmod them.  So don't give files away.  */
409
410           if ((owner != (uid_t)-1 || group != (gid_t)-1)
411               && chown(dirpath, owner, group)
412 #ifdef AFS
413               && errno != EPERM
414 #endif
415               ) {
416               berrno be;
417               Jmsg(jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
418                      quote(dirpath), be.bstrerror());
419             }
420           if (chmod(dirpath, mode)) {
421               berrno be;
422               Jmsg(jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
423                                  quote(dirpath), be.bstrerror());
424           }
425           Dmsg2(300, "pathexists chmod mode=%o dir=%s\n", mode, dirpath);
426       }
427   }
428   return retval;
429 }