]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/mkpath.c
Eliminate complier warning
[bacula/bacula] / bacula / src / findlib / mkpath.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28
29 /*
30  *  Kern Sibbald, September MMVII
31  * 
32  *  This is tricky code, especially when writing from scratch. Fortunately,
33  *    a non-copyrighted version of mkdir was available to consult.
34  *
35  * ***FIXME*** the mkpath code could be significantly optimized by
36  *   walking up the path chain from the bottom until it either gets
37  *   to the top or finds an existing directory then walk back down
38  *   creating the path components.  Currently, it always starts at
39  *   the top, which can be rather inefficient for long path names.
40  *
41  */
42 #include "bacula.h"
43 #include "jcr.h"
44
45 static bool makedir(JCR *jcr, char *path, mode_t mode, int *created)
46 {
47    struct stat statp;
48
49    if (mkdir(path, mode) != 0) {
50       berrno be;
51       *created = false;
52       if (stat(path, &statp) != 0) {
53          Jmsg2(jcr, M_ERROR, 0, _("Cannot create directory %s: ERR=%s\n"),
54               path, be.bstrerror());
55          return false;
56       } else if (!S_ISDIR(statp.st_mode)) {
57          Jmsg1(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
58          return false;
59       }
60       return true;                 /* directory exists */
61    }
62    *created = true;
63    return true;
64 }
65
66 static void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t mode)
67 {
68    if (chown(path, owner, group) != 0 && attr->uid == 0
69 #ifdef AFS
70         && errno != EPERM
71 #endif
72    ) {
73       berrno be;
74       Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
75            path, be.bstrerror());
76    }
77    if (chmod(path, mode) != 0 && attr->uid == 0) {
78       berrno be;
79       Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
80            path, be.bstrerror());
81    }
82 }
83
84 /*
85  * mode is the mode bits to use in creating a new directory
86  *
87  * parent_mode are the parent's modes if we need to create parent 
88  *    directories.
89  *
90  * owner and group are to set on any created dirs
91  *
92  * keep_dir_modes if set means don't change mode bits if dir exists
93  */
94 bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode,
95             uid_t owner, gid_t group, int keep_dir_modes)
96 {
97    struct stat statp;
98    mode_t omask, tmode;
99    char *path = (char *)apath;
100    char *p;
101    int len;
102    bool ok = false;
103    int created;
104    char new_dir[5000];
105    int ndir = 0;
106    int i = 0;
107    int max_dirs = (int)sizeof(new_dir);
108    JCR *jcr = attr->jcr;
109
110    if (stat(path, &statp) == 0) {     /* Does dir exist? */
111       if (!S_ISDIR(statp.st_mode)) {
112          Jmsg1(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
113          return false;
114       }
115       /* Full path exists */
116       if (keep_dir_modes) {
117          return true;
118       }
119       set_own_mod(attr, path, owner, group, mode);
120       return true;
121    }
122    omask = umask(0);
123    umask(omask);
124    len = strlen(apath);
125    path = (char *)alloca(len+1);
126    bstrncpy(path, apath, len+1);
127    strip_trailing_slashes(path);
128    /*
129     * Now for one of the complexities. If we are not running as root,
130     *  then if the parent_mode does not have wx user perms, or we are
131     *  setting the userid or group, and the parent_mode has setuid, setgid,
132     *  or sticky bits, we must create the dir with open permissions, then
133     *  go back and patch all the dirs up with the correct perms.
134     * Solution, set everything to 0777, then go back and reset them at the
135     *  end.
136     */
137    tmode = 0777;
138
139 #if defined(HAVE_WIN32)
140    /* Validate drive letter */
141    if (path[1] == ':') {
142       char drive[4] = "X:\\";
143
144       drive[0] = path[0];
145
146       UINT drive_type = GetDriveType(drive);
147
148       if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) {
149          Jmsg1(jcr, M_ERROR, 0, _("%c: is not a valid drive.\n"), path[0]);
150          goto bail_out;
151       }
152
153       if (path[2] == '\0') {          /* attempt to create a drive */
154          ok = true;
155          goto bail_out;               /* OK, it is already there */
156       }
157
158       p = &path[3];
159    } else {
160       p = path;
161    }
162 #else
163    p = path;
164 #endif
165
166    /* Skip leading slash(es) */
167    while (IsPathSeparator(*p)) {
168       p++;
169    }
170    while ((p = first_path_separator(p))) {
171       char save_p;
172       save_p = *p;
173       *p = 0;
174       if (!makedir(jcr, path, tmode, &created)) {
175          goto bail_out;
176       }
177       Dmsg2(400, "makedir: created=%d %s\n", created, path);
178       if (ndir < max_dirs) {
179          new_dir[ndir++] = created;
180       }
181       *p = save_p;
182       while (IsPathSeparator(*p)) {
183          p++;
184       }
185    }
186    /* Create final component */
187    if (!makedir(jcr, path, tmode, &created)) {
188       goto bail_out;
189    }
190    Dmsg2(400, "makedir: created=%d %s\n", created, path);
191    if (ndir < max_dirs) {
192       new_dir[ndir++] = created;
193    }
194    if (ndir >= max_dirs) {
195       Jmsg0(jcr, M_WARNING, 0, _("Too many subdirectories. Some permissions not reset.\n"));
196    }
197
198    /* Now set the proper owner and modes */
199 #if defined(HAVE_WIN32)
200    if (path[1] == ':') {
201       p = &path[3];
202    } else {
203       p = path;
204    }
205 #else
206    p = path;
207 #endif
208    /* Skip leading slash(es) */
209    while (IsPathSeparator(*p)) {
210       p++;
211    }
212    while ((p = first_path_separator(p))) {
213       char save_p;
214       save_p = *p;
215       *p = 0;
216       if (i < ndir && new_dir[i++] && !keep_dir_modes) {
217          set_own_mod(attr, path, owner, group, parent_mode);
218       }
219       *p = save_p;
220       while (IsPathSeparator(*p)) {
221          p++;
222       }
223    }
224
225    /* Set for final component */
226    if (i < ndir && new_dir[i++]) {
227       set_own_mod(attr, path, owner, group, mode);
228    }
229
230    ok = true;
231 bail_out:
232    umask(omask);
233    return ok;
234 }