1 |
|
|
/* |
2 |
|
|
* Copyright (c) 1990 Jan-Simon Pendry |
3 |
|
|
* Copyright (c) 1990 Imperial College of Science, Technology & Medicine |
4 |
|
|
* Copyright (c) 1990, 1993 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* |
7 |
|
|
* This code is derived from software contributed to Berkeley by |
8 |
|
|
* Jan-Simon Pendry at Imperial College, London. |
9 |
|
|
* |
10 |
|
|
* Redistribution and use in source and binary forms, with or without |
11 |
|
|
* modification, are permitted provided that the following conditions |
12 |
|
|
* are met: |
13 |
|
|
* 1. Redistributions of source code must retain the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer. |
15 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
16 |
|
|
* notice, this list of conditions and the following disclaimer in the |
17 |
|
|
* documentation and/or other materials provided with the distribution. |
18 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
19 |
|
|
* may be used to endorse or promote products derived from this software |
20 |
|
|
* without specific prior written permission. |
21 |
|
|
* |
22 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 |
|
|
* SUCH DAMAGE. |
33 |
|
|
* |
34 |
|
|
* from: @(#)util.c 8.1 (Berkeley) 6/6/93 |
35 |
|
|
* $Id: util.c,v 1.16 2015/12/12 20:06:42 mmcc Exp $ |
36 |
|
|
*/ |
37 |
|
|
|
38 |
|
|
/* |
39 |
|
|
* Utils |
40 |
|
|
*/ |
41 |
|
|
|
42 |
|
|
#include "am.h" |
43 |
|
|
#include <ctype.h> |
44 |
|
|
#include <unistd.h> |
45 |
|
|
#include <sys/stat.h> |
46 |
|
|
#include <netdb.h> |
47 |
|
|
|
48 |
|
|
|
49 |
|
|
char * |
50 |
|
|
strnsave(const char *str, int len) |
51 |
|
|
{ |
52 |
|
|
char *sp = xmalloc(len+1); |
53 |
|
|
|
54 |
|
|
bcopy(str, sp, len); |
55 |
|
|
sp[len] = 0; |
56 |
|
|
|
57 |
|
|
return sp; |
58 |
|
|
} |
59 |
|
|
|
60 |
|
|
/* |
61 |
|
|
* Concatenate three strings and store in buffer pointed to |
62 |
|
|
* by p, making p large enough to hold the strings |
63 |
|
|
*/ |
64 |
|
|
char * |
65 |
|
|
str3cat(char *p, char *s1, char *s2, char *s3) |
66 |
|
|
{ |
67 |
|
|
size_t l1 = strlen(s1); |
68 |
|
|
size_t l2 = strlen(s2); |
69 |
|
|
size_t l3 = strlen(s3); |
70 |
|
|
|
71 |
|
|
if (l1 > SIZE_MAX - l2 || l1 + l2 > SIZE_MAX - l3) |
72 |
|
|
xmallocfailure(); |
73 |
|
|
p = xreallocarray(p, l1 + l2 + l3 + 1, 1); |
74 |
|
|
bcopy(s1, p, l1); |
75 |
|
|
bcopy(s2, p + l1, l2); |
76 |
|
|
bcopy(s3, p + l1 + l2, l3 + 1); |
77 |
|
|
return p; |
78 |
|
|
} |
79 |
|
|
|
80 |
|
|
char * |
81 |
|
|
strealloc(char *p, char *s) |
82 |
|
|
{ |
83 |
|
|
size_t len = strlen(s) + 1; |
84 |
|
|
|
85 |
|
|
if (len > SIZE_MAX - 1) |
86 |
|
|
xmallocfailure(); |
87 |
|
|
p = xreallocarray(p, len, 1); |
88 |
|
|
|
89 |
|
|
strlcpy(p, s, len); |
90 |
|
|
return p; |
91 |
|
|
} |
92 |
|
|
|
93 |
|
|
char ** |
94 |
|
|
strsplit(char *s, int ch, int qc) |
95 |
|
|
{ |
96 |
|
|
char **ivec; |
97 |
|
|
int ic = 0; |
98 |
|
|
int done = 0; |
99 |
|
|
|
100 |
|
|
ivec = xreallocarray(NULL, ic + 1, sizeof *ivec); |
101 |
|
|
|
102 |
|
|
while (!done) { |
103 |
|
|
char *v; |
104 |
|
|
/* |
105 |
|
|
* skip to split char |
106 |
|
|
*/ |
107 |
|
|
while (*s && (ch == ' ' ? |
108 |
|
|
(isascii((unsigned char)*s) && isspace((unsigned char)*s)) : |
109 |
|
|
*s == ch)) |
110 |
|
|
*s++ = '\0'; |
111 |
|
|
|
112 |
|
|
/* |
113 |
|
|
* End of string? |
114 |
|
|
*/ |
115 |
|
|
if (!*s) |
116 |
|
|
break; |
117 |
|
|
|
118 |
|
|
/* |
119 |
|
|
* remember start of string |
120 |
|
|
*/ |
121 |
|
|
v = s; |
122 |
|
|
|
123 |
|
|
/* |
124 |
|
|
* skip to split char |
125 |
|
|
*/ |
126 |
|
|
while (*s && !(ch == ' ' ? |
127 |
|
|
(isascii((unsigned char)*s) && isspace((unsigned char)*s)) : |
128 |
|
|
*s == ch)) { |
129 |
|
|
if (*s++ == qc) { |
130 |
|
|
/* |
131 |
|
|
* Skip past string. |
132 |
|
|
*/ |
133 |
|
|
s++; |
134 |
|
|
while (*s && *s != qc) |
135 |
|
|
s++; |
136 |
|
|
if (*s == qc) |
137 |
|
|
s++; |
138 |
|
|
} |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
if (!*s) |
142 |
|
|
done = 1; |
143 |
|
|
*s++ = '\0'; |
144 |
|
|
|
145 |
|
|
/* |
146 |
|
|
* save string in new ivec slot |
147 |
|
|
*/ |
148 |
|
|
ivec[ic++] = v; |
149 |
|
|
ivec = xreallocarray(ivec, ic + 1, sizeof *ivec); |
150 |
|
|
#ifdef DEBUG |
151 |
|
|
Debug(D_STR) |
152 |
|
|
plog(XLOG_DEBUG, "strsplit saved \"%s\"", v); |
153 |
|
|
#endif /* DEBUG */ |
154 |
|
|
} |
155 |
|
|
|
156 |
|
|
#ifdef DEBUG |
157 |
|
|
Debug(D_STR) |
158 |
|
|
plog(XLOG_DEBUG, "strsplit saved a total of %d strings", ic); |
159 |
|
|
#endif /* DEBUG */ |
160 |
|
|
|
161 |
|
|
ivec[ic] = 0; |
162 |
|
|
|
163 |
|
|
return ivec; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
/* |
167 |
|
|
* Strip off the trailing part of a domain |
168 |
|
|
* to produce a short-form domain relative |
169 |
|
|
* to the local host domain. |
170 |
|
|
* Note that this has no effect if the domain |
171 |
|
|
* names do not have the same number of |
172 |
|
|
* components. If that restriction proves |
173 |
|
|
* to be a problem then the loop needs recoding |
174 |
|
|
* to skip from right to left and do partial |
175 |
|
|
* matches along the way -- ie more expensive. |
176 |
|
|
*/ |
177 |
|
|
static void |
178 |
|
|
domain_strip(char *otherdom, char *localdom) |
179 |
|
|
{ |
180 |
|
|
#ifdef PARTIAL_DOMAINS |
181 |
|
|
char *p1 = otherdom-1; |
182 |
|
|
char *p2 = localdom-1; |
183 |
|
|
|
184 |
|
|
do { |
185 |
|
|
if (p1 = strchr(p1+1, '.')) |
186 |
|
|
if (p2 = strchr(p2+1, '.')) |
187 |
|
|
if (strcmp(p1+1, p2+1) == 0) { |
188 |
|
|
*p1 = '\0'; |
189 |
|
|
break; |
190 |
|
|
} |
191 |
|
|
} while (p1 && p2); |
192 |
|
|
#else |
193 |
|
|
char *p1, *p2; |
194 |
|
|
|
195 |
|
|
if ((p1 = strchr(otherdom, '.')) && |
196 |
|
|
(p2 = strchr(localdom, '.')) && |
197 |
|
|
(strcmp(p1+1, p2+1) == 0)) |
198 |
|
|
*p1 = '\0'; |
199 |
|
|
#endif /* PARTIAL_DOMAINS */ |
200 |
|
|
} |
201 |
|
|
|
202 |
|
|
/* |
203 |
|
|
* Normalize a host name |
204 |
|
|
*/ |
205 |
|
|
void |
206 |
|
|
host_normalize(char **chp) |
207 |
|
|
{ |
208 |
|
|
/* |
209 |
|
|
* Normalize hosts is used to resolve host name aliases |
210 |
|
|
* and replace them with the standard-form name. |
211 |
|
|
* Invoked with "-n" command line option. |
212 |
|
|
*/ |
213 |
|
|
if (normalize_hosts) { |
214 |
|
|
struct hostent *hp; |
215 |
|
|
clock_valid = 0; |
216 |
|
|
hp = gethostbyname(*chp); |
217 |
|
|
if (hp && hp->h_addrtype == AF_INET) { |
218 |
|
|
#ifdef DEBUG |
219 |
|
|
dlog("Hostname %s normalized to %s", *chp, hp->h_name); |
220 |
|
|
#endif /* DEBUG */ |
221 |
|
|
*chp = strealloc(*chp, hp->h_name); |
222 |
|
|
} |
223 |
|
|
} |
224 |
|
|
domain_strip(*chp, hostd); |
225 |
|
|
} |
226 |
|
|
|
227 |
|
|
/* |
228 |
|
|
* Make a dotted quad from a 32bit IP address |
229 |
|
|
* addr is in network byte order. |
230 |
|
|
* sizeof(buf) needs to be at least 16. |
231 |
|
|
*/ |
232 |
|
|
char * |
233 |
|
|
inet_dquad(char *buf, size_t buflen, u_int32_t addr) |
234 |
|
|
{ |
235 |
|
|
addr = ntohl(addr); |
236 |
|
|
snprintf(buf, buflen, "%d.%d.%d.%d", |
237 |
|
|
((addr >> 24) & 0xff), |
238 |
|
|
((addr >> 16) & 0xff), |
239 |
|
|
((addr >> 8) & 0xff), |
240 |
|
|
((addr >> 0) & 0xff)); |
241 |
|
|
return buf; |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
/* |
245 |
|
|
* Keys are not allowed to contain " ' ! or ; to avoid |
246 |
|
|
* problems with macro expansions. |
247 |
|
|
*/ |
248 |
|
|
static char invalid_keys[] = "\"'!;@ \t\n"; |
249 |
|
|
int |
250 |
|
|
valid_key(char *key) |
251 |
|
|
{ |
252 |
|
|
while (*key) |
253 |
|
|
if (strchr(invalid_keys, *key++)) |
254 |
|
|
return FALSE; |
255 |
|
|
return TRUE; |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
void |
259 |
|
|
going_down(int rc) |
260 |
|
|
{ |
261 |
|
|
if (foreground) { |
262 |
|
|
if (amd_state != Start) { |
263 |
|
|
if (amd_state != Done) |
264 |
|
|
return; |
265 |
|
|
unregister_amq(); |
266 |
|
|
} |
267 |
|
|
} |
268 |
|
|
if (foreground) { |
269 |
|
|
plog(XLOG_INFO, "Finishing with status %d", rc); |
270 |
|
|
} else { |
271 |
|
|
#ifdef DEBUG |
272 |
|
|
dlog("background process exiting with status %d", rc); |
273 |
|
|
#endif /* DEBUG */ |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
exit(rc); |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
|
280 |
|
|
int |
281 |
|
|
bind_resv_port(int so, u_short *pp) |
282 |
|
|
{ |
283 |
|
|
struct sockaddr_in sin; |
284 |
|
|
int rc; |
285 |
|
|
|
286 |
|
|
bzero(&sin, sizeof(sin)); |
287 |
|
|
sin.sin_family = AF_INET; |
288 |
|
|
|
289 |
|
|
rc = bindresvport(so, &sin); |
290 |
|
|
if (pp && rc == 0) |
291 |
|
|
*pp = ntohs(sin.sin_port); |
292 |
|
|
return rc; |
293 |
|
|
} |
294 |
|
|
|
295 |
|
|
void |
296 |
|
|
forcibly_timeout_mp(am_node *mp) |
297 |
|
|
{ |
298 |
|
|
mntfs *mf = mp->am_mnt; |
299 |
|
|
/* |
300 |
|
|
* Arrange to timeout this node |
301 |
|
|
*/ |
302 |
|
|
if (mf && ((mp->am_flags & AMF_ROOT) || |
303 |
|
|
(mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)))) { |
304 |
|
|
if (!(mf->mf_flags & MFF_UNMOUNTING)) |
305 |
|
|
plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path); |
306 |
|
|
} else { |
307 |
|
|
plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path); |
308 |
|
|
mp->am_flags &= ~AMF_NOTIMEOUT; |
309 |
|
|
mp->am_ttl = clocktime(); |
310 |
|
|
reschedule_timeout_mp(); |
311 |
|
|
} |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
void |
315 |
|
|
mf_mounted(mntfs *mf) |
316 |
|
|
{ |
317 |
|
|
int quoted; |
318 |
|
|
int wasmounted = mf->mf_flags & MFF_MOUNTED; |
319 |
|
|
|
320 |
|
|
if (!wasmounted) { |
321 |
|
|
/* |
322 |
|
|
* If this is a freshly mounted |
323 |
|
|
* filesystem then update the |
324 |
|
|
* mntfs structure... |
325 |
|
|
*/ |
326 |
|
|
mf->mf_flags |= MFF_MOUNTED; |
327 |
|
|
mf->mf_error = 0; |
328 |
|
|
|
329 |
|
|
/* |
330 |
|
|
* Do mounted callback |
331 |
|
|
*/ |
332 |
|
|
if (mf->mf_ops->mounted) |
333 |
|
|
(*mf->mf_ops->mounted)(mf); |
334 |
|
|
|
335 |
|
|
mf->mf_fo = 0; |
336 |
|
|
} |
337 |
|
|
|
338 |
|
|
/* |
339 |
|
|
* Log message |
340 |
|
|
*/ |
341 |
|
|
quoted = strchr(mf->mf_info, ' ') != 0; |
342 |
|
|
plog(XLOG_INFO, "%s%s%s %s fstype %s on %s", |
343 |
|
|
quoted ? "\"" : "", |
344 |
|
|
mf->mf_info, |
345 |
|
|
quoted ? "\"" : "", |
346 |
|
|
wasmounted ? "referenced" : "mounted", |
347 |
|
|
mf->mf_ops->fs_type, mf->mf_mount); |
348 |
|
|
} |
349 |
|
|
|
350 |
|
|
void |
351 |
|
|
am_mounted(am_node *mp) |
352 |
|
|
{ |
353 |
|
|
mntfs *mf = mp->am_mnt; |
354 |
|
|
|
355 |
|
|
mf_mounted(mf); |
356 |
|
|
|
357 |
|
|
/* |
358 |
|
|
* Patch up path for direct mounts |
359 |
|
|
*/ |
360 |
|
|
if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &dfs_ops) |
361 |
|
|
mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); |
362 |
|
|
|
363 |
|
|
/* |
364 |
|
|
* Check whether this mount should be cached permanently |
365 |
|
|
*/ |
366 |
|
|
if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) { |
367 |
|
|
mp->am_flags |= AMF_NOTIMEOUT; |
368 |
|
|
} else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') { |
369 |
|
|
mp->am_flags |= AMF_NOTIMEOUT; |
370 |
|
|
} else { |
371 |
|
|
struct mntent mnt; |
372 |
|
|
if (mf->mf_mopts) { |
373 |
|
|
mnt.mnt_opts = mf->mf_mopts; |
374 |
|
|
if (hasmntopt(&mnt, "nounmount")) |
375 |
|
|
mp->am_flags |= AMF_NOTIMEOUT; |
376 |
|
|
if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) |
377 |
|
|
mp->am_timeo = am_timeo; |
378 |
|
|
} |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
/* |
382 |
|
|
* If this node is a symlink then |
383 |
|
|
* compute the length of the returned string. |
384 |
|
|
*/ |
385 |
|
|
if (mp->am_fattr.type == NFLNK) |
386 |
|
|
mp->am_fattr.size = strlen(mp->am_link ? mp->am_link : mp->am_mnt->mf_mount); |
387 |
|
|
|
388 |
|
|
/* |
389 |
|
|
* Record mount time |
390 |
|
|
*/ |
391 |
|
|
mp->am_fattr.mtime.seconds = mp->am_stats.s_mtime = clocktime(); |
392 |
|
|
new_ttl(mp); |
393 |
|
|
/* |
394 |
|
|
* Update mtime of parent node |
395 |
|
|
*/ |
396 |
|
|
if (mp->am_parent && mp->am_parent->am_mnt) |
397 |
|
|
mp->am_parent->am_fattr.mtime.seconds = mp->am_stats.s_mtime; |
398 |
|
|
|
399 |
|
|
|
400 |
|
|
/* |
401 |
|
|
* Update stats |
402 |
|
|
*/ |
403 |
|
|
amd_stats.d_mok++; |
404 |
|
|
} |
405 |
|
|
|
406 |
|
|
int |
407 |
|
|
mount_node(am_node *mp) |
408 |
|
|
{ |
409 |
|
|
mntfs *mf = mp->am_mnt; |
410 |
|
|
int error; |
411 |
|
|
|
412 |
|
|
mf->mf_flags |= MFF_MOUNTING; |
413 |
|
|
error = (*mf->mf_ops->mount_fs)(mp); |
414 |
|
|
mf = mp->am_mnt; |
415 |
|
|
if (error >= 0) |
416 |
|
|
mf->mf_flags &= ~MFF_MOUNTING; |
417 |
|
|
if (!error && !(mf->mf_ops->fs_flags & FS_MBACKGROUND)) { |
418 |
|
|
/* ...but see ifs_mount */ |
419 |
|
|
am_mounted(mp); |
420 |
|
|
} |
421 |
|
|
|
422 |
|
|
return error; |
423 |
|
|
} |
424 |
|
|
|
425 |
|
|
void |
426 |
|
|
am_unmounted(am_node *mp) |
427 |
|
|
{ |
428 |
|
|
mntfs *mf = mp->am_mnt; |
429 |
|
|
|
430 |
|
|
if (!foreground) /* firewall - should never happen */ |
431 |
|
|
return; |
432 |
|
|
|
433 |
|
|
#ifdef DEBUG |
434 |
|
|
/*dlog("in am_unmounted(), foreground = %d", foreground);*/ |
435 |
|
|
#endif /* DEBUG */ |
436 |
|
|
|
437 |
|
|
/* |
438 |
|
|
* Do unmounted callback |
439 |
|
|
*/ |
440 |
|
|
if (mf->mf_ops->umounted) |
441 |
|
|
(*mf->mf_ops->umounted)(mp); |
442 |
|
|
|
443 |
|
|
/* |
444 |
|
|
* Update mtime of parent node |
445 |
|
|
*/ |
446 |
|
|
if (mp->am_parent && mp->am_parent->am_mnt) |
447 |
|
|
mp->am_parent->am_fattr.mtime.seconds = clocktime(); |
448 |
|
|
|
449 |
|
|
free_map(mp); |
450 |
|
|
} |
451 |
|
|
|
452 |
|
|
int |
453 |
|
|
auto_fmount(am_node *mp) |
454 |
|
|
{ |
455 |
|
|
mntfs *mf = mp->am_mnt; |
456 |
|
|
return (*mf->mf_ops->fmount_fs)(mf); |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
int |
460 |
|
|
auto_fumount(am_node *mp) |
461 |
|
|
{ |
462 |
|
|
mntfs *mf = mp->am_mnt; |
463 |
|
|
return (*mf->mf_ops->fumount_fs)(mf); |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
/* |
467 |
|
|
* Fork the automounter |
468 |
|
|
* |
469 |
|
|
* TODO: Need a better strategy for handling errors |
470 |
|
|
*/ |
471 |
|
|
static pid_t |
472 |
|
|
dofork(void) |
473 |
|
|
{ |
474 |
|
|
pid_t pid; |
475 |
|
|
top: |
476 |
|
|
pid = fork(); |
477 |
|
|
|
478 |
|
|
if (pid < 0) { |
479 |
|
|
sleep(1); |
480 |
|
|
goto top; |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
if (pid == 0) { |
484 |
|
|
mypid = getpid(); |
485 |
|
|
foreground = 0; |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
return pid; |
489 |
|
|
} |
490 |
|
|
|
491 |
|
|
pid_t |
492 |
|
|
background(void) |
493 |
|
|
{ |
494 |
|
|
pid_t pid = dofork(); |
495 |
|
|
if (pid == 0) { |
496 |
|
|
#ifdef DEBUG |
497 |
|
|
dlog("backgrounded"); |
498 |
|
|
#endif |
499 |
|
|
foreground = 0; |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
return pid; |
503 |
|
|
} |
504 |
|
|
|
505 |
|
|
/* |
506 |
|
|
* Make all the directories in the path. |
507 |
|
|
*/ |
508 |
|
|
int |
509 |
|
|
mkdirs(char *path, int mode) |
510 |
|
|
{ |
511 |
|
|
/* |
512 |
|
|
* take a copy in case path is in readonly store |
513 |
|
|
*/ |
514 |
|
|
char *p2 = strdup(path); |
515 |
|
|
char *sp = p2; |
516 |
|
|
struct stat stb; |
517 |
|
|
int error_so_far = 0; |
518 |
|
|
|
519 |
|
|
/* |
520 |
|
|
* Skip through the string make the directories. |
521 |
|
|
* Mostly ignore errors - the result is tested at the end. |
522 |
|
|
* |
523 |
|
|
* This assumes we are root so that we can do mkdir in a |
524 |
|
|
* mode 555 directory... |
525 |
|
|
*/ |
526 |
|
|
while ((sp = strchr(sp+1, '/'))) { |
527 |
|
|
*sp = '\0'; |
528 |
|
|
if (mkdir(p2, mode) < 0) { |
529 |
|
|
error_so_far = errno; |
530 |
|
|
} else { |
531 |
|
|
#ifdef DEBUG |
532 |
|
|
dlog("mkdir(%s)", p2); |
533 |
|
|
#endif |
534 |
|
|
} |
535 |
|
|
*sp = '/'; |
536 |
|
|
} |
537 |
|
|
|
538 |
|
|
if (mkdir(p2, mode) < 0) { |
539 |
|
|
error_so_far = errno; |
540 |
|
|
} else { |
541 |
|
|
#ifdef DEBUG |
542 |
|
|
dlog("mkdir(%s)", p2); |
543 |
|
|
#endif |
544 |
|
|
} |
545 |
|
|
|
546 |
|
|
|
547 |
|
|
free(p2); |
548 |
|
|
|
549 |
|
|
return stat(path, &stb) == 0 && |
550 |
|
|
(stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far; |
551 |
|
|
} |
552 |
|
|
|
553 |
|
|
/* |
554 |
|
|
* Remove as many directories in the path as possible. |
555 |
|
|
* Give up if the directory doesn't appear to have |
556 |
|
|
* been created by Amd (not mode dr-x) or an rmdir |
557 |
|
|
* fails for any reason. |
558 |
|
|
*/ |
559 |
|
|
void |
560 |
|
|
rmdirs(char *dir) |
561 |
|
|
{ |
562 |
|
|
char *xdp = strdup(dir); |
563 |
|
|
char *dp; |
564 |
|
|
|
565 |
|
|
do { |
566 |
|
|
struct stat stb; |
567 |
|
|
/* |
568 |
|
|
* Try to find out whether this was |
569 |
|
|
* created by amd. Do this by checking |
570 |
|
|
* for owner write permission. |
571 |
|
|
*/ |
572 |
|
|
if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) { |
573 |
|
|
if (rmdir(xdp) < 0) { |
574 |
|
|
if (errno != ENOTEMPTY && |
575 |
|
|
errno != EBUSY && |
576 |
|
|
errno != EEXIST && |
577 |
|
|
errno != EINVAL) |
578 |
|
|
plog(XLOG_ERROR, "rmdir(%s): %m", xdp); |
579 |
|
|
break; |
580 |
|
|
} else { |
581 |
|
|
#ifdef DEBUG |
582 |
|
|
dlog("rmdir(%s)", xdp); |
583 |
|
|
#endif |
584 |
|
|
} |
585 |
|
|
} else { |
586 |
|
|
break; |
587 |
|
|
} |
588 |
|
|
dp = strrchr(xdp, '/'); |
589 |
|
|
if (dp) |
590 |
|
|
*dp = '\0'; |
591 |
|
|
} while (dp && dp > xdp); |
592 |
|
|
free(xdp); |
593 |
|
|
} |