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 *
strnsave(const char * str,int len)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 *
str3cat(char * p,char * s1,char * s2,char * s3)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 *
strealloc(char * p,char * s)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 **
strsplit(char * s,int ch,int qc)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
domain_strip(char * otherdom,char * localdom)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
host_normalize(char ** chp)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 *
inet_dquad(char * buf,size_t buflen,u_int32_t addr)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
valid_key(char * key)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
going_down(int rc)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
bind_resv_port(int so,u_short * pp)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
forcibly_timeout_mp(am_node * mp)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
mf_mounted(mntfs * mf)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
am_mounted(am_node * mp)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
mount_node(am_node * mp)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
am_unmounted(am_node * mp)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
auto_fmount(am_node * mp)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
auto_fumount(am_node * mp)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
dofork(void)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
background(void)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
mkdirs(char * path,int mode)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
rmdirs(char * dir)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 }
594