1 /*-
2  * Copyright (c) 1990, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1990, 1993, 1994\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)mail.local.c	8.17 (Berkeley) 01/25/95";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21 
22 #include <netinet/in.h>
23 
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <netdb.h>
27 #include <pwd.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sysexits.h>
32 #include <syslog.h>
33 #include <time.h>
34 #include <unistd.h>
35 #include <ctype.h>
36 
37 #if __STDC__
38 #include <stdarg.h>
39 #else
40 #include <varargs.h>
41 #endif
42 
43 #ifndef LOCK_EX
44 # include <sys/file.h>
45 #endif
46 
47 #ifdef BSD4_4
48 # include "pathnames.h"
49 #endif
50 
51 #ifndef __P
52 # ifdef __STDC__
53 #  define __P(protos)	protos
54 # else
55 #  define __P(protos)	()
56 #  define const
57 # endif
58 #endif
59 #ifndef __dead
60 # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
61 #  define __dead	__volatile
62 # else
63 #  define __dead
64 # endif
65 #endif
66 
67 #ifndef BSD4_4
68 # define _BSD_VA_LIST_	va_list
69 extern char	*strerror __P((int));
70 #endif
71 
72 #ifndef _PATH_LOCTMP
73 # define _PATH_LOCTMP	"/tmp/local.XXXXXX"
74 #endif
75 #ifndef _PATH_MAILDIR
76 # define _PATH_MAILDIR	"/var/spool/mail"
77 #endif
78 
79 #ifndef S_ISREG
80 # define S_ISREG(mode)	(((mode) & _S_IFMT) == S_IFREG)
81 #endif
82 
83 int eval = EX_OK;			/* sysexits.h error value. */
84 
85 void		deliver __P((int, char *));
86 void		e_to_sys __P((int));
87 __dead void	err __P((const char *, ...));
88 void		notifybiff __P((char *));
89 int		store __P((char *));
90 void		usage __P((void));
91 void		vwarn __P((const char *, _BSD_VA_LIST_));
92 void		warn __P((const char *, ...));
93 
94 int
95 main(argc, argv)
96 	int argc;
97 	char *argv[];
98 {
99 	struct passwd *pw;
100 	int ch, fd;
101 	uid_t uid;
102 	char *from;
103 	extern char *optarg;
104 	extern int optind;
105 
106 	/* make sure we have some open file descriptors */
107 	for (fd = 10; fd < 30; fd++)
108 		(void) close(fd);
109 
110 	/* use a reasonable umask */
111 	(void) umask(0077);
112 
113 #ifdef LOG_MAIL
114 	openlog("mail.local", 0, LOG_MAIL);
115 #else
116 	openlog("mail.local", 0);
117 #endif
118 
119 	from = NULL;
120 	while ((ch = getopt(argc, argv, "df:r:")) != EOF)
121 		switch(ch) {
122 		case 'd':		/* Backward compatible. */
123 			break;
124 		case 'f':
125 		case 'r':		/* Backward compatible. */
126 			if (from != NULL) {
127 				warn("multiple -f options");
128 				usage();
129 			}
130 			from = optarg;
131 			break;
132 		case '?':
133 		default:
134 			usage();
135 		}
136 	argc -= optind;
137 	argv += optind;
138 
139 	if (!*argv)
140 		usage();
141 
142 	/*
143 	 * If from not specified, use the name from getlogin() if the
144 	 * uid matches, otherwise, use the name from the password file
145 	 * corresponding to the uid.
146 	 */
147 	uid = getuid();
148 	if (!from && (!(from = getlogin()) ||
149 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
150 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
151 
152 	/*
153 	 * There is no way to distinguish the error status of one delivery
154 	 * from the rest of the deliveries.  So, if we failed hard on one
155 	 * or more deliveries, but had no failures on any of the others, we
156 	 * return a hard failure.  If we failed temporarily on one or more
157 	 * deliveries, we return a temporary failure regardless of the other
158 	 * failures.  This results in the delivery being reattempted later
159 	 * at the expense of repeated failures and multiple deliveries.
160 	 */
161 	for (fd = store(from); *argv; ++argv)
162 		deliver(fd, *argv);
163 	exit(eval);
164 }
165 
166 int
167 store(from)
168 	char *from;
169 {
170 	FILE *fp;
171 	time_t tval;
172 	int fd, eline;
173 	char line[2048];
174 	char tmpbuf[sizeof _PATH_LOCTMP + 1];
175 
176 	strcpy(tmpbuf, _PATH_LOCTMP);
177 	if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
178 		e_to_sys(errno);
179 		err("unable to open temporary file");
180 	}
181 	(void)unlink(tmpbuf);
182 
183 	(void)time(&tval);
184 	(void)fprintf(fp, "From %s %s", from, ctime(&tval));
185 
186 	line[0] = '\0';
187 	for (eline = 1; fgets(line, sizeof(line), stdin);) {
188 		if (line[0] == '\n')
189 			eline = 1;
190 		else {
191 			if (eline && line[0] == 'F' &&
192 			    !memcmp(line, "From ", 5))
193 				(void)putc('>', fp);
194 			eline = 0;
195 		}
196 		(void)fprintf(fp, "%s", line);
197 		if (ferror(fp)) {
198 			e_to_sys(errno);
199 			err("temporary file write error");
200 		}
201 	}
202 
203 	/* If message not newline terminated, need an extra. */
204 	if (!strchr(line, '\n'))
205 		(void)putc('\n', fp);
206 	/* Output a newline; note, empty messages are allowed. */
207 	(void)putc('\n', fp);
208 
209 	if (fflush(fp) == EOF || ferror(fp)) {
210 		e_to_sys(errno);
211 		err("temporary file write error");
212 	}
213 	return (fd);
214 }
215 
216 void
217 deliver(fd, name)
218 	int fd;
219 	char *name;
220 {
221 	struct stat fsb, sb;
222 	struct passwd *pw;
223 	int mbfd, nr, nw, off;
224 	char *p;
225 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
226 	off_t curoff;
227 
228 	/*
229 	 * Disallow delivery to unknown names -- special mailboxes can be
230 	 * handled in the sendmail aliases file.
231 	 */
232 	if (!(pw = getpwnam(name))) {
233 		if (eval != EX_TEMPFAIL)
234 			eval = EX_UNAVAILABLE;
235 		warn("unknown name: %s", name);
236 		return;
237 	}
238 
239 	/*
240 	 * Keep name reasonably short to avoid buffer overruns.
241 	 *	This isn't necessary on BSD because of the proper
242 	 *	definition of snprintf(), but it can cause problems
243 	 *	on other systems.
244 	 * Also, clear out any bogus characters.
245 	 */
246 
247 	if (strlen(name) > 40)
248 		name[40] = '\0';
249 	for (p = name; *p != '\0'; p++)
250 	{
251 		if (!isascii(*p))
252 			*p &= 0x7f;
253 		else if (!isprint(*p))
254 			*p = '.';
255 	}
256 
257 	(void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
258 
259 	/*
260 	 * If the mailbox is linked or a symlink, fail.  There's an obvious
261 	 * race here, that the file was replaced with a symbolic link after
262 	 * the lstat returned, but before the open.  We attempt to detect
263 	 * this by comparing the original stat information and information
264 	 * returned by an fstat of the file descriptor returned by the open.
265 	 *
266 	 * NB: this is a symptom of a larger problem, that the mail spooling
267 	 * directory is writeable by the wrong users.  If that directory is
268 	 * writeable, system security is compromised for other reasons, and
269 	 * it cannot be fixed here.
270 	 *
271 	 * If we created the mailbox, set the owner/group.  If that fails,
272 	 * just return.  Another process may have already opened it, so we
273 	 * can't unlink it.  Historically, binmail set the owner/group at
274 	 * each mail delivery.  We no longer do this, assuming that if the
275 	 * ownership or permissions were changed there was a reason.
276 	 *
277 	 * XXX
278 	 * open(2) should support flock'ing the file.
279 	 */
280 tryagain:
281 	lockmbox(path);
282 	if (lstat(path, &sb)) {
283 		mbfd = open(path,
284 		    O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
285 		if (mbfd == -1) {
286 			if (errno == EEXIST)
287 				goto tryagain;
288 		} else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
289 			e_to_sys(errno);
290 			warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
291 			unlockmbox();
292 			return;
293 		}
294 	} else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
295 		e_to_sys(errno);
296 		warn("%s: irregular file", path);
297 		unlockmbox();
298 		return;
299 	} else if (sb.st_uid != pw->pw_uid) {
300 		warn("%s: wrong ownership (%d)", path, sb.st_uid);
301 		unlockmbox();
302 		return;
303 	} else {
304 		mbfd = open(path, O_APPEND|O_WRONLY, 0);
305 		if (mbfd != -1 &&
306 		    (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
307 		    !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
308 		    sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) {
309 			warn("%s: file changed after open", path);
310 			(void)close(mbfd);
311 			unlockmbox();
312 			return;
313 		}
314 	}
315 
316 	if (mbfd == -1) {
317 		e_to_sys(errno);
318 		warn("%s: %s", path, strerror(errno));
319 		unlockmbox();
320 		return;
321 	}
322 
323 	/* Wait until we can get a lock on the file. */
324 	if (flock(mbfd, LOCK_EX)) {
325 		e_to_sys(errno);
326 		warn("%s: %s", path, strerror(errno));
327 		unlockmbox();
328 		goto err1;
329 	}
330 
331 	/* Get the starting offset of the new message for biff. */
332 	curoff = lseek(mbfd, (off_t)0, SEEK_END);
333 	(void)snprintf(biffmsg, sizeof(biffmsg),
334 		sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n",
335 		name, curoff);
336 
337 	/* Copy the message into the file. */
338 	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
339 		e_to_sys(errno);
340 		warn("temporary file: %s", strerror(errno));
341 		goto err1;
342 	}
343 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
344 		for (off = 0; off < nr; off += nw)
345 			if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
346 				e_to_sys(errno);
347 				warn("%s: %s", path, strerror(errno));
348 				goto err2;;
349 			}
350 	if (nr < 0) {
351 		e_to_sys(errno);
352 		warn("temporary file: %s", strerror(errno));
353 		goto err2;;
354 	}
355 
356 	/* Flush to disk, don't wait for update. */
357 	if (fsync(mbfd)) {
358 		e_to_sys(errno);
359 		warn("%s: %s", path, strerror(errno));
360 err2:		(void)ftruncate(mbfd, curoff);
361 err1:		(void)close(mbfd);
362 		unlockmbox();
363 		return;
364 	}
365 
366 	/* Close and check -- NFS doesn't write until the close. */
367 	if (close(mbfd)) {
368 		e_to_sys(errno);
369 		warn("%s: %s", path, strerror(errno));
370 		unlockmbox();
371 		return;
372 	}
373 
374 	unlockmbox();
375 	notifybiff(biffmsg);
376 }
377 
378 /*
379  * user.lock files are necessary for compatibility with other
380  * systems, e.g., when the mail spool file is NFS exported.
381  * Alas, mailbox locking is more than just a local matter.
382  * EPA 11/94.
383  */
384 
385 char	lockname[MAXPATHLEN];
386 int	locked = 0;
387 
388 lockmbox(path)
389 	char *path;
390 {
391 	int statfailed = 0;
392 
393 	if (locked)
394 		return;
395 	sprintf(lockname, "%s.lock", path);
396 	for (;; sleep(5)) {
397 		int fd;
398 		struct stat st;
399 		time_t now;
400 
401 		fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
402 		if (fd >= 0) {
403 			locked = 1;
404 			close(fd);
405 			return;
406 		}
407 		if (stat(lockname, &st) < 0) {
408 			if (statfailed++ > 5)
409 				return;
410 			continue;
411 		}
412 		statfailed = 0;
413 		time(&now);
414 		if (now < st.st_ctime + 300)
415 			continue;
416 		unlink(lockname);
417 	}
418 }
419 
420 unlockmbox()
421 {
422 	if (!locked)
423 		return;
424 	unlink(lockname);
425 	locked = 0;
426 }
427 
428 void
429 notifybiff(msg)
430 	char *msg;
431 {
432 	static struct sockaddr_in addr;
433 	static int f = -1;
434 	struct hostent *hp;
435 	struct servent *sp;
436 	int len;
437 
438 	if (!addr.sin_family) {
439 		/* Be silent if biff service not available. */
440 		if (!(sp = getservbyname("biff", "udp")))
441 			return;
442 		if (!(hp = gethostbyname("localhost"))) {
443 			warn("localhost: %s", strerror(errno));
444 			return;
445 		}
446 		addr.sin_family = hp->h_addrtype;
447 		memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
448 		addr.sin_port = sp->s_port;
449 	}
450 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
451 		warn("socket: %s", strerror(errno));
452 		return;
453 	}
454 	len = strlen(msg) + 1;
455 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
456 	    != len)
457 		warn("sendto biff: %s", strerror(errno));
458 }
459 
460 void
461 usage()
462 {
463 	eval = EX_USAGE;
464 	err("usage: mail.local [-f from] user ...");
465 }
466 
467 #if __STDC__
468 void
469 err(const char *fmt, ...)
470 #else
471 void
472 err(fmt, va_alist)
473 	const char *fmt;
474 	va_dcl
475 #endif
476 {
477 	va_list ap;
478 
479 #if __STDC__
480 	va_start(ap, fmt);
481 #else
482 	va_start(ap);
483 #endif
484 	vwarn(fmt, ap);
485 	va_end(ap);
486 
487 	exit(eval);
488 }
489 
490 void
491 #if __STDC__
492 warn(const char *fmt, ...)
493 #else
494 warn(fmt, va_alist)
495 	const char *fmt;
496 	va_dcl
497 #endif
498 {
499 	va_list ap;
500 
501 #if __STDC__
502 	va_start(ap, fmt);
503 #else
504 	va_start(ap);
505 #endif
506 	vwarn(fmt, ap);
507 	va_end(ap);
508 }
509 
510 void
511 vwarn(fmt, ap)
512 	const char *fmt;
513 	_BSD_VA_LIST_ ap;
514 {
515 	/*
516 	 * Log the message to stderr.
517 	 *
518 	 * Don't use LOG_PERROR as an openlog() flag to do this,
519 	 * it's not portable enough.
520 	 */
521 	if (eval != EX_USAGE)
522 		(void)fprintf(stderr, "mail.local: ");
523 	(void)vfprintf(stderr, fmt, ap);
524 	(void)fprintf(stderr, "\n");
525 
526 #ifndef ultrix
527 	/* Log the message to syslog. */
528 	vsyslog(LOG_ERR, fmt, ap);
529 #else
530 	{
531 		char fmtbuf[10240];
532 
533 		(void) sprintf(fmtbuf, fmt, ap);
534 		syslog(LOG_ERR, "%s", fmtbuf);
535 	}
536 #endif
537 }
538 
539 /*
540  * e_to_sys --
541  *	Guess which errno's are temporary.  Gag me.
542  */
543 void
544 e_to_sys(num)
545 	int num;
546 {
547 	/* Temporary failures override hard errors. */
548 	if (eval == EX_TEMPFAIL)
549 		return;
550 
551 	switch(num) {		/* Hopefully temporary errors. */
552 #ifdef EAGAIN
553 	case EAGAIN:		/* Resource temporarily unavailable */
554 #endif
555 #ifdef EDQUOT
556 	case EDQUOT:		/* Disc quota exceeded */
557 #endif
558 #ifdef EBUSY
559 	case EBUSY:		/* Device busy */
560 #endif
561 #ifdef EPROCLIM
562 	case EPROCLIM:		/* Too many processes */
563 #endif
564 #ifdef EUSERS
565 	case EUSERS:		/* Too many users */
566 #endif
567 #ifdef ECONNABORTED
568 	case ECONNABORTED:	/* Software caused connection abort */
569 #endif
570 #ifdef ECONNREFUSED
571 	case ECONNREFUSED:	/* Connection refused */
572 #endif
573 #ifdef ECONNRESET
574 	case ECONNRESET:	/* Connection reset by peer */
575 #endif
576 #ifdef EDEADLK
577 	case EDEADLK:		/* Resource deadlock avoided */
578 #endif
579 #ifdef EFBIG
580 	case EFBIG:		/* File too large */
581 #endif
582 #ifdef EHOSTDOWN
583 	case EHOSTDOWN:		/* Host is down */
584 #endif
585 #ifdef EHOSTUNREACH
586 	case EHOSTUNREACH:	/* No route to host */
587 #endif
588 #ifdef EMFILE
589 	case EMFILE:		/* Too many open files */
590 #endif
591 #ifdef ENETDOWN
592 	case ENETDOWN:		/* Network is down */
593 #endif
594 #ifdef ENETRESET
595 	case ENETRESET:		/* Network dropped connection on reset */
596 #endif
597 #ifdef ENETUNREACH
598 	case ENETUNREACH:	/* Network is unreachable */
599 #endif
600 #ifdef ENFILE
601 	case ENFILE:		/* Too many open files in system */
602 #endif
603 #ifdef ENOBUFS
604 	case ENOBUFS:		/* No buffer space available */
605 #endif
606 #ifdef ENOMEM
607 	case ENOMEM:		/* Cannot allocate memory */
608 #endif
609 #ifdef ENOSPC
610 	case ENOSPC:		/* No space left on device */
611 #endif
612 #ifdef EROFS
613 	case EROFS:		/* Read-only file system */
614 #endif
615 #ifdef ESTALE
616 	case ESTALE:		/* Stale NFS file handle */
617 #endif
618 #ifdef ETIMEDOUT
619 	case ETIMEDOUT:		/* Connection timed out */
620 #endif
621 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
622 	case EWOULDBLOCK:	/* Operation would block. */
623 #endif
624 		eval = EX_TEMPFAIL;
625 		break;
626 	default:
627 		eval = EX_UNAVAILABLE;
628 		break;
629 	}
630 }
631 
632 #ifndef BSD4_4
633 
634 char *
635 strerror(eno)
636 	int eno;
637 {
638 	extern int sys_nerr;
639 	extern char *sys_errlist[];
640 	static char ebuf[60];
641 
642 	if (eno >= 0 && eno <= sys_nerr)
643 		return sys_errlist[eno];
644 	(void) sprintf(ebuf, "Error %d", eno);
645 	return ebuf;
646 }
647 
648 #if __STDC__
649 snprintf(char *buf, int bufsiz, const char *fmt, ...)
650 #else
651 snprintf(buf, bufsiz, fmt, va_alist)
652 	char *buf;
653 	int bufsiz;
654 	const char *fmt;
655 	va_dcl
656 #endif
657 {
658 	va_list ap;
659 
660 #if __STDC__
661 	va_start(ap, fmt);
662 #else
663 	va_start(ap);
664 #endif
665 	vsprintf(buf, fmt, ap);
666 	va_end(ap);
667 }
668 
669 #endif
670 
671 #ifdef ultrix
672 
673 /*
674  * Copyright (c) 1987, 1993
675  *	The Regents of the University of California.  All rights reserved.
676  *
677  * Redistribution and use in source and binary forms, with or without
678  * modification, are permitted provided that the following conditions
679  * are met:
680  * 1. Redistributions of source code must retain the above copyright
681  *    notice, this list of conditions and the following disclaimer.
682  * 2. Redistributions in binary form must reproduce the above copyright
683  *    notice, this list of conditions and the following disclaimer in the
684  *    documentation and/or other materials provided with the distribution.
685  * 3. All advertising materials mentioning features or use of this software
686  *    must display the following acknowledgement:
687  *	This product includes software developed by the University of
688  *	California, Berkeley and its contributors.
689  * 4. Neither the name of the University nor the names of its contributors
690  *    may be used to endorse or promote products derived from this software
691  *    without specific prior written permission.
692  *
693  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
694  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
695  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
696  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
697  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
698  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
699  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
700  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
701  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
702  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
703  * SUCH DAMAGE.
704  */
705 
706 #if defined(LIBC_SCCS) && !defined(lint)
707 static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
708 #endif /* LIBC_SCCS and not lint */
709 
710 #include <sys/types.h>
711 #include <sys/stat.h>
712 #include <fcntl.h>
713 #include <errno.h>
714 #include <stdio.h>
715 #include <ctype.h>
716 
717 static int _gettemp();
718 
719 mkstemp(path)
720 	char *path;
721 {
722 	int fd;
723 
724 	return (_gettemp(path, &fd) ? fd : -1);
725 }
726 
727 /*
728 char *
729 mktemp(path)
730 	char *path;
731 {
732 	return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
733 }
734 */
735 
736 static
737 _gettemp(path, doopen)
738 	char *path;
739 	register int *doopen;
740 {
741 	extern int errno;
742 	register char *start, *trv;
743 	struct stat sbuf;
744 	u_int pid;
745 
746 	pid = getpid();
747 	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
748 	while (*--trv == 'X') {
749 		*trv = (pid % 10) + '0';
750 		pid /= 10;
751 	}
752 
753 	/*
754 	 * check the target directory; if you have six X's and it
755 	 * doesn't exist this runs for a *very* long time.
756 	 */
757 	for (start = trv + 1;; --trv) {
758 		if (trv <= path)
759 			break;
760 		if (*trv == '/') {
761 			*trv = '\0';
762 			if (stat(path, &sbuf))
763 				return(0);
764 			if (!S_ISDIR(sbuf.st_mode)) {
765 				errno = ENOTDIR;
766 				return(0);
767 			}
768 			*trv = '/';
769 			break;
770 		}
771 	}
772 
773 	for (;;) {
774 		if (doopen) {
775 			if ((*doopen =
776 			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
777 				return(1);
778 			if (errno != EEXIST)
779 				return(0);
780 		}
781 		else if (stat(path, &sbuf))
782 			return(errno == ENOENT ? 1 : 0);
783 
784 		/* tricky little algorithm for backward compatibility */
785 		for (trv = start;;) {
786 			if (!*trv)
787 				return(0);
788 			if (*trv == 'z')
789 				*trv++ = 'a';
790 			else {
791 				if (isdigit(*trv))
792 					*trv = 'a';
793 				else
794 					++*trv;
795 				break;
796 			}
797 		}
798 	}
799 	/*NOTREACHED*/
800 }
801 
802 #endif
803