Remove tm.h and xm.h handling, as it wasn't used. Use nm.h only when needed.
[dragonfly.git] / contrib / sendmail-8.14 / libsmutil / safefile.c
blob8488534280e3ba039ed73d6497ed42786e52ffd7
1 /*
2 * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
14 #include <sendmail.h>
15 #include <sm/io.h>
16 #include <sm/errstring.h>
18 SM_RCSID("@(#)$Id: safefile.c,v 8.128 2004/09/30 18:15:49 ca Exp $")
22 ** SAFEFILE -- return 0 if a file exists and is safe for a user.
24 ** Parameters:
25 ** fn -- filename to check.
26 ** uid -- user id to compare against.
27 ** gid -- group id to compare against.
28 ** user -- user name to compare against (used for group
29 ** sets).
30 ** flags -- modifiers:
31 ** SFF_MUSTOWN -- "uid" must own this file.
32 ** SFF_NOSLINK -- file cannot be a symbolic link.
33 ** mode -- mode bits that must match.
34 ** st -- if set, points to a stat structure that will
35 ** get the stat info for the file.
37 ** Returns:
38 ** 0 if fn exists, is owned by uid, and matches mode.
39 ** An errno otherwise. The actual errno is cleared.
41 ** Side Effects:
42 ** none.
45 int
46 safefile(fn, uid, gid, user, flags, mode, st)
47 char *fn;
48 UID_T uid;
49 GID_T gid;
50 char *user;
51 long flags;
52 int mode;
53 struct stat *st;
55 register char *p;
56 register struct group *gr = NULL;
57 int file_errno = 0;
58 bool checkpath;
59 struct stat stbuf;
60 struct stat fstbuf;
61 char fbuf[MAXPATHLEN];
63 if (tTd(44, 4))
64 sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
65 fn, (int) uid, (int) gid, flags, mode);
66 errno = 0;
67 if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
69 if (tTd(44, 4))
70 sm_dprintf("\tpathname too long\n");
71 return ENAMETOOLONG;
73 fn = fbuf;
74 if (st == NULL)
75 st = &fstbuf;
77 /* ignore SFF_SAFEDIRPATH if we are debugging */
78 if (RealUid != 0 && RunAsUid == RealUid)
79 flags &= ~SFF_SAFEDIRPATH;
81 /* first check to see if the file exists at all */
82 # if HASLSTAT
83 if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
84 : stat(fn, st)) < 0)
85 # else /* HASLSTAT */
86 if (stat(fn, st) < 0)
87 # endif /* HASLSTAT */
89 file_errno = errno;
91 else if (bitset(SFF_SETUIDOK, flags) &&
92 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
93 S_ISREG(st->st_mode))
96 ** If final file is set-user-ID, run as the owner of that
97 ** file. Gotta be careful not to reveal anything too
98 ** soon here!
101 # ifdef SUID_ROOT_FILES_OK
102 if (bitset(S_ISUID, st->st_mode))
103 # else /* SUID_ROOT_FILES_OK */
104 if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
105 st->st_uid != TrustedUid)
106 # endif /* SUID_ROOT_FILES_OK */
108 uid = st->st_uid;
109 user = NULL;
111 # ifdef SUID_ROOT_FILES_OK
112 if (bitset(S_ISGID, st->st_mode))
113 # else /* SUID_ROOT_FILES_OK */
114 if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
115 # endif /* SUID_ROOT_FILES_OK */
116 gid = st->st_gid;
119 checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
120 (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
121 if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
123 int ret;
125 /* check the directory */
126 p = strrchr(fn, '/');
127 if (p == NULL)
129 ret = safedirpath(".", uid, gid, user,
130 flags|SFF_SAFEDIRPATH, 0, 0);
132 else
134 *p = '\0';
135 ret = safedirpath(fn, uid, gid, user,
136 flags|SFF_SAFEDIRPATH, 0, 0);
137 *p = '/';
139 if (ret == 0)
141 /* directory is safe */
142 checkpath = false;
144 else
146 # if HASLSTAT
147 /* Need lstat() information if called stat() before */
148 if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
150 ret = errno;
151 if (tTd(44, 4))
152 sm_dprintf("\t%s\n", sm_errstring(ret));
153 return ret;
155 # endif /* HASLSTAT */
156 /* directory is writable: disallow links */
157 flags |= SFF_NOLINK;
161 if (checkpath)
163 int ret;
165 p = strrchr(fn, '/');
166 if (p == NULL)
168 ret = safedirpath(".", uid, gid, user, flags, 0, 0);
170 else
172 *p = '\0';
173 ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
174 *p = '/';
176 if (ret != 0)
177 return ret;
181 ** If the target file doesn't exist, check the directory to
182 ** ensure that it is writable by this user.
185 if (file_errno != 0)
187 int ret = file_errno;
188 char *dir = fn;
190 if (tTd(44, 4))
191 sm_dprintf("\t%s\n", sm_errstring(ret));
193 errno = 0;
194 if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
195 return ret;
197 /* check to see if legal to create the file */
198 p = strrchr(dir, '/');
199 if (p == NULL)
200 dir = ".";
201 else if (p == dir)
202 dir = "/";
203 else
204 *p = '\0';
205 if (stat(dir, &stbuf) >= 0)
207 int md = S_IWRITE|S_IEXEC;
209 ret = 0;
210 if (stbuf.st_uid == uid)
211 /* EMPTY */
213 else if (uid == 0 && stbuf.st_uid == TrustedUid)
214 /* EMPTY */
216 else
218 md >>= 3;
219 if (stbuf.st_gid == gid)
220 /* EMPTY */
222 # ifndef NO_GROUP_SET
223 else if (user != NULL && !DontInitGroups &&
224 ((gr != NULL &&
225 gr->gr_gid == stbuf.st_gid) ||
226 (gr = getgrgid(stbuf.st_gid)) != NULL))
228 register char **gp;
230 for (gp = gr->gr_mem; *gp != NULL; gp++)
231 if (strcmp(*gp, user) == 0)
232 break;
233 if (*gp == NULL)
234 md >>= 3;
236 # endif /* ! NO_GROUP_SET */
237 else
238 md >>= 3;
240 if ((stbuf.st_mode & md) != md)
241 ret = errno = EACCES;
243 else
244 ret = errno;
245 if (tTd(44, 4))
246 sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n",
247 dir, (int) stbuf.st_uid,
248 (unsigned long) stbuf.st_mode,
249 sm_errstring(ret));
250 if (p != NULL)
251 *p = '/';
252 st->st_mode = ST_MODE_NOFILE;
253 return ret;
256 # ifdef S_ISLNK
257 if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
259 if (tTd(44, 4))
260 sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
261 (unsigned long) st->st_mode);
262 return E_SM_NOSLINK;
264 # endif /* S_ISLNK */
265 if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
267 if (tTd(44, 4))
268 sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
269 (unsigned long) st->st_mode);
270 return E_SM_REGONLY;
272 if (bitset(SFF_NOGWFILES, flags) &&
273 bitset(S_IWGRP, st->st_mode))
275 if (tTd(44, 4))
276 sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
277 (unsigned long) st->st_mode);
278 return E_SM_GWFILE;
280 if (bitset(SFF_NOWWFILES, flags) &&
281 bitset(S_IWOTH, st->st_mode))
283 if (tTd(44, 4))
284 sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
285 (unsigned long) st->st_mode);
286 return E_SM_WWFILE;
288 if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
290 if (tTd(44, 4))
291 sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
292 (unsigned long) st->st_mode);
293 return E_SM_GRFILE;
295 if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
297 if (tTd(44, 4))
298 sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
299 (unsigned long) st->st_mode);
300 return E_SM_WRFILE;
302 if (!bitset(SFF_EXECOK, flags) &&
303 bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
304 bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
306 if (tTd(44, 4))
307 sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n",
308 (unsigned long) st->st_mode);
309 return E_SM_ISEXEC;
311 if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
313 if (tTd(44, 4))
314 sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
315 (int) st->st_nlink);
316 return E_SM_NOHLINK;
319 if (uid == 0 && bitset(SFF_OPENASROOT, flags))
320 /* EMPTY */
322 else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
323 mode >>= 6;
324 else if (st->st_uid == uid)
325 /* EMPTY */
327 else if (uid == 0 && st->st_uid == TrustedUid)
328 /* EMPTY */
330 else
332 mode >>= 3;
333 if (st->st_gid == gid)
334 /* EMPTY */
336 # ifndef NO_GROUP_SET
337 else if (user != NULL && !DontInitGroups &&
338 ((gr != NULL && gr->gr_gid == st->st_gid) ||
339 (gr = getgrgid(st->st_gid)) != NULL))
341 register char **gp;
343 for (gp = gr->gr_mem; *gp != NULL; gp++)
344 if (strcmp(*gp, user) == 0)
345 break;
346 if (*gp == NULL)
347 mode >>= 3;
349 # endif /* ! NO_GROUP_SET */
350 else
351 mode >>= 3;
353 if (tTd(44, 4))
354 sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
355 (int) st->st_uid, (int) st->st_nlink,
356 (unsigned long) st->st_mode, (unsigned long) mode);
357 if ((st->st_uid == uid || st->st_uid == 0 ||
358 st->st_uid == TrustedUid ||
359 !bitset(SFF_MUSTOWN, flags)) &&
360 (st->st_mode & mode) == mode)
362 if (tTd(44, 4))
363 sm_dprintf("\tOK\n");
364 return 0;
366 if (tTd(44, 4))
367 sm_dprintf("\tEACCES\n");
368 return EACCES;
371 ** SAFEDIRPATH -- check to make sure a path to a directory is safe
373 ** Safe means not writable and owned by the right folks.
375 ** Parameters:
376 ** fn -- filename to check.
377 ** uid -- user id to compare against.
378 ** gid -- group id to compare against.
379 ** user -- user name to compare against (used for group
380 ** sets).
381 ** flags -- modifiers:
382 ** SFF_ROOTOK -- ok to use root permissions to open.
383 ** SFF_SAFEDIRPATH -- writable directories are considered
384 ** to be fatal errors.
385 ** level -- symlink recursive level.
386 ** offset -- offset into fn to start checking from.
388 ** Returns:
389 ** 0 -- if the directory path is "safe".
390 ** else -- an error number associated with the path.
394 safedirpath(fn, uid, gid, user, flags, level, offset)
395 char *fn;
396 UID_T uid;
397 GID_T gid;
398 char *user;
399 long flags;
400 int level;
401 int offset;
403 int ret = 0;
404 int mode = S_IWOTH;
405 char save = '\0';
406 char *saveptr = NULL;
407 char *p, *enddir;
408 register struct group *gr = NULL;
409 char s[MAXLINKPATHLEN];
410 struct stat stbuf;
412 /* make sure we aren't in a symlink loop */
413 if (level > MAXSYMLINKS)
414 return ELOOP;
416 if (level < 0 || offset < 0 || offset > strlen(fn))
417 return EINVAL;
419 /* special case root directory */
420 if (*fn == '\0')
421 fn = "/";
423 if (tTd(44, 4))
424 sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
425 fn, (long) uid, (long) gid, flags, level, offset);
427 if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
428 mode |= S_IWGRP;
430 /* Make a modifiable copy of the filename */
431 if (sm_strlcpy(s, fn, sizeof s) >= sizeof s)
432 return EINVAL;
434 p = s + offset;
435 while (p != NULL)
437 /* put back character */
438 if (saveptr != NULL)
440 *saveptr = save;
441 saveptr = NULL;
442 p++;
445 if (*p == '\0')
446 break;
448 p = strchr(p, '/');
450 /* Special case for root directory */
451 if (p == s)
453 save = *(p + 1);
454 saveptr = p + 1;
455 *(p + 1) = '\0';
457 else if (p != NULL)
459 save = *p;
460 saveptr = p;
461 *p = '\0';
464 /* Heuristic: . and .. have already been checked */
465 enddir = strrchr(s, '/');
466 if (enddir != NULL &&
467 (strcmp(enddir, "/..") == 0 ||
468 strcmp(enddir, "/.") == 0))
469 continue;
471 if (tTd(44, 20))
472 sm_dprintf("\t[dir %s]\n", s);
474 # if HASLSTAT
475 ret = lstat(s, &stbuf);
476 # else /* HASLSTAT */
477 ret = stat(s, &stbuf);
478 # endif /* HASLSTAT */
479 if (ret < 0)
481 ret = errno;
482 break;
485 # ifdef S_ISLNK
486 /* Follow symlinks */
487 if (S_ISLNK(stbuf.st_mode))
489 int linklen;
490 char *target;
491 char buf[MAXPATHLEN];
492 char fullbuf[MAXLINKPATHLEN];
494 memset(buf, '\0', sizeof buf);
495 linklen = readlink(s, buf, sizeof buf);
496 if (linklen < 0)
498 ret = errno;
499 break;
501 if (linklen >= sizeof buf)
503 /* file name too long for buffer */
504 ret = errno = EINVAL;
505 break;
508 offset = 0;
509 if (*buf == '/')
511 target = buf;
513 /* If path is the same, avoid rechecks */
514 while (s[offset] == buf[offset] &&
515 s[offset] != '\0')
516 offset++;
518 if (s[offset] == '\0' && buf[offset] == '\0')
520 /* strings match, symlink loop */
521 return ELOOP;
524 /* back off from the mismatch */
525 if (offset > 0)
526 offset--;
528 /* Make sure we are at a directory break */
529 if (offset > 0 &&
530 s[offset] != '/' &&
531 s[offset] != '\0')
533 while (buf[offset] != '/' &&
534 offset > 0)
535 offset--;
537 if (offset > 0 &&
538 s[offset] == '/' &&
539 buf[offset] == '/')
541 /* Include the trailing slash */
542 offset++;
545 else
547 char *sptr;
549 sptr = strrchr(s, '/');
550 if (sptr != NULL)
552 *sptr = '\0';
553 offset = sptr + 1 - s;
554 if (sm_strlcpyn(fullbuf,
555 sizeof fullbuf, 2,
556 s, "/") >=
557 sizeof fullbuf ||
558 sm_strlcat(fullbuf, buf,
559 sizeof fullbuf) >=
560 sizeof fullbuf)
562 ret = EINVAL;
563 break;
565 *sptr = '/';
567 else
569 if (sm_strlcpy(fullbuf, buf,
570 sizeof fullbuf) >=
571 sizeof fullbuf)
573 ret = EINVAL;
574 break;
577 target = fullbuf;
579 ret = safedirpath(target, uid, gid, user, flags,
580 level + 1, offset);
581 if (ret != 0)
582 break;
584 /* Don't check permissions on the link file itself */
585 continue;
587 #endif /* S_ISLNK */
589 if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
590 #ifdef S_ISVTX
591 !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
592 bitset(S_ISVTX, stbuf.st_mode)) &&
593 #endif /* S_ISVTX */
594 bitset(mode, stbuf.st_mode))
596 if (tTd(44, 4))
597 sm_dprintf("\t[dir %s] mode %lo ",
598 s, (unsigned long) stbuf.st_mode);
599 if (bitset(SFF_SAFEDIRPATH, flags))
601 if (bitset(S_IWOTH, stbuf.st_mode))
602 ret = E_SM_WWDIR;
603 else
604 ret = E_SM_GWDIR;
605 if (tTd(44, 4))
606 sm_dprintf("FATAL\n");
607 break;
609 if (tTd(44, 4))
610 sm_dprintf("WARNING\n");
611 if (Verbose > 1)
612 message("051 WARNING: %s writable directory %s",
613 bitset(S_IWOTH, stbuf.st_mode)
614 ? "World"
615 : "Group",
618 if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
620 if (bitset(S_IXOTH, stbuf.st_mode))
621 continue;
622 ret = EACCES;
623 break;
627 ** Let OS determine access to file if we are not
628 ** running as a privileged user. This allows ACLs
629 ** to work. Also, if opening as root, assume we can
630 ** scan the directory.
632 if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
633 continue;
635 if (stbuf.st_uid == uid &&
636 bitset(S_IXUSR, stbuf.st_mode))
637 continue;
638 if (stbuf.st_gid == gid &&
639 bitset(S_IXGRP, stbuf.st_mode))
640 continue;
641 # ifndef NO_GROUP_SET
642 if (user != NULL && !DontInitGroups &&
643 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
644 (gr = getgrgid(stbuf.st_gid)) != NULL))
646 register char **gp;
648 for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
649 if (strcmp(*gp, user) == 0)
650 break;
651 if (gp != NULL && *gp != NULL &&
652 bitset(S_IXGRP, stbuf.st_mode))
653 continue;
655 # endif /* ! NO_GROUP_SET */
656 if (!bitset(S_IXOTH, stbuf.st_mode))
658 ret = EACCES;
659 break;
662 if (tTd(44, 4))
663 sm_dprintf("\t[dir %s] %s\n", fn,
664 ret == 0 ? "OK" : sm_errstring(ret));
665 return ret;
668 ** SAFEOPEN -- do a file open with extra checking
670 ** Parameters:
671 ** fn -- the file name to open.
672 ** omode -- the open-style mode flags.
673 ** cmode -- the create-style mode flags.
674 ** sff -- safefile flags.
676 ** Returns:
677 ** Same as open.
681 safeopen(fn, omode, cmode, sff)
682 char *fn;
683 int omode;
684 int cmode;
685 long sff;
687 #if !NOFTRUNCATE
688 bool truncate;
689 #endif /* !NOFTRUNCATE */
690 int rval;
691 int fd;
692 int smode;
693 struct stat stb;
695 if (tTd(44, 10))
696 sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
697 fn, omode, cmode, sff);
699 if (bitset(O_CREAT, omode))
700 sff |= SFF_CREAT;
701 omode &= ~O_CREAT;
702 smode = 0;
703 switch (omode & O_ACCMODE)
705 case O_RDONLY:
706 smode = S_IREAD;
707 break;
709 case O_WRONLY:
710 smode = S_IWRITE;
711 break;
713 case O_RDWR:
714 smode = S_IREAD|S_IWRITE;
715 break;
717 default:
718 smode = 0;
719 break;
721 if (bitset(SFF_OPENASROOT, sff))
722 rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
723 sff, smode, &stb);
724 else
725 rval = safefile(fn, RealUid, RealGid, RealUserName,
726 sff, smode, &stb);
727 if (rval != 0)
729 errno = rval;
730 return -1;
732 if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
733 omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
734 else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
736 /* The file exists so an exclusive create would fail */
737 errno = EEXIST;
738 return -1;
741 #if !NOFTRUNCATE
742 truncate = bitset(O_TRUNC, omode);
743 if (truncate)
744 omode &= ~O_TRUNC;
745 #endif /* !NOFTRUNCATE */
747 fd = dfopen(fn, omode, cmode, sff);
748 if (fd < 0)
749 return fd;
750 if (filechanged(fn, fd, &stb))
752 syserr("554 5.3.0 cannot open: file %s changed after open", fn);
753 (void) close(fd);
754 errno = E_SM_FILECHANGE;
755 return -1;
758 #if !NOFTRUNCATE
759 if (truncate &&
760 ftruncate(fd, (off_t) 0) < 0)
762 int save_errno;
764 save_errno = errno;
765 syserr("554 5.3.0 cannot open: file %s could not be truncated",
766 fn);
767 (void) close(fd);
768 errno = save_errno;
769 return -1;
771 #endif /* !NOFTRUNCATE */
773 return fd;
776 ** SAFEFOPEN -- do a file open with extra checking
778 ** Parameters:
779 ** fn -- the file name to open.
780 ** omode -- the open-style mode flags.
781 ** cmode -- the create-style mode flags.
782 ** sff -- safefile flags.
784 ** Returns:
785 ** Same as fopen.
788 SM_FILE_T *
789 safefopen(fn, omode, cmode, sff)
790 char *fn;
791 int omode;
792 int cmode;
793 long sff;
795 int fd;
796 int save_errno;
797 SM_FILE_T *fp;
798 int fmode;
800 switch (omode & O_ACCMODE)
802 case O_RDONLY:
803 fmode = SM_IO_RDONLY;
804 break;
806 case O_WRONLY:
807 if (bitset(O_APPEND, omode))
808 fmode = SM_IO_APPEND;
809 else
810 fmode = SM_IO_WRONLY;
811 break;
813 case O_RDWR:
814 if (bitset(O_TRUNC, omode))
815 fmode = SM_IO_RDWRTR;
816 else if (bitset(O_APPEND, omode))
817 fmode = SM_IO_APPENDRW;
818 else
819 fmode = SM_IO_RDWR;
820 break;
822 default:
823 syserr("554 5.3.5 safefopen: unknown omode %o", omode);
824 fmode = 0;
826 fd = safeopen(fn, omode, cmode, sff);
827 if (fd < 0)
829 save_errno = errno;
830 if (tTd(44, 10))
831 sm_dprintf("safefopen: safeopen failed: %s\n",
832 sm_errstring(errno));
833 errno = save_errno;
834 return NULL;
836 fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
837 (void *) &fd, fmode, NULL);
838 if (fp != NULL)
839 return fp;
841 save_errno = errno;
842 if (tTd(44, 10))
844 sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
845 fn, fmode, omode, sff, sm_errstring(errno));
847 (void) close(fd);
848 errno = save_errno;
849 return NULL;
852 ** FILECHANGED -- check to see if file changed after being opened
854 ** Parameters:
855 ** fn -- pathname of file to check.
856 ** fd -- file descriptor to check.
857 ** stb -- stat structure from before open.
859 ** Returns:
860 ** true -- if a problem was detected.
861 ** false -- if this file is still the same.
864 bool
865 filechanged(fn, fd, stb)
866 char *fn;
867 int fd;
868 struct stat *stb;
870 struct stat sta;
872 if (stb->st_mode == ST_MODE_NOFILE)
874 # if HASLSTAT && BOGUS_O_EXCL
875 /* only necessary if exclusive open follows symbolic links */
876 if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
877 return true;
878 # else /* HASLSTAT && BOGUS_O_EXCL */
879 return false;
880 # endif /* HASLSTAT && BOGUS_O_EXCL */
882 if (fstat(fd, &sta) < 0)
883 return true;
885 if (sta.st_nlink != stb->st_nlink ||
886 sta.st_dev != stb->st_dev ||
887 sta.st_ino != stb->st_ino ||
888 # if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
889 sta.st_gen != stb->st_gen ||
890 # endif /* HAS_ST_GEN && 0 */
891 sta.st_uid != stb->st_uid ||
892 sta.st_gid != stb->st_gid)
894 if (tTd(44, 8))
896 sm_dprintf("File changed after opening:\n");
897 sm_dprintf(" nlink = %ld/%ld\n",
898 (long) stb->st_nlink, (long) sta.st_nlink);
899 sm_dprintf(" dev = %ld/%ld\n",
900 (long) stb->st_dev, (long) sta.st_dev);
901 sm_dprintf(" ino = %llu/%llu\n",
902 (ULONGLONG_T) stb->st_ino,
903 (ULONGLONG_T) sta.st_ino);
904 # if HAS_ST_GEN
905 sm_dprintf(" gen = %ld/%ld\n",
906 (long) stb->st_gen, (long) sta.st_gen);
907 # endif /* HAS_ST_GEN */
908 sm_dprintf(" uid = %ld/%ld\n",
909 (long) stb->st_uid, (long) sta.st_uid);
910 sm_dprintf(" gid = %ld/%ld\n",
911 (long) stb->st_gid, (long) sta.st_gid);
913 return true;
916 return false;
919 ** DFOPEN -- determined file open
921 ** This routine has the semantics of open, except that it will
922 ** keep trying a few times to make this happen. The idea is that
923 ** on very loaded systems, we may run out of resources (inodes,
924 ** whatever), so this tries to get around it.
928 dfopen(filename, omode, cmode, sff)
929 char *filename;
930 int omode;
931 int cmode;
932 long sff;
934 register int tries;
935 int fd = -1;
936 struct stat st;
938 for (tries = 0; tries < 10; tries++)
940 (void) sleep((unsigned) (10 * tries));
941 errno = 0;
942 fd = open(filename, omode, cmode);
943 if (fd >= 0)
944 break;
945 switch (errno)
947 case ENFILE: /* system file table full */
948 case EINTR: /* interrupted syscall */
949 #ifdef ETXTBSY
950 case ETXTBSY: /* Apollo: net file locked */
951 #endif /* ETXTBSY */
952 continue;
954 break;
956 if (!bitset(SFF_NOLOCK, sff) &&
957 fd >= 0 &&
958 fstat(fd, &st) >= 0 &&
959 S_ISREG(st.st_mode))
961 int locktype;
963 /* lock the file to avoid accidental conflicts */
964 if ((omode & O_ACCMODE) != O_RDONLY)
965 locktype = LOCK_EX;
966 else
967 locktype = LOCK_SH;
968 if (bitset(SFF_NBLOCK, sff))
969 locktype |= LOCK_NB;
971 if (!lockfile(fd, filename, NULL, locktype))
973 int save_errno = errno;
975 (void) close(fd);
976 fd = -1;
977 errno = save_errno;
979 else
980 errno = 0;
982 return fd;