sys/subr_rman: fix some issues
[dragonfly.git] / contrib / sendmail-8.14 / libsmutil / safefile.c
blobf299e1053556ffe81265a64df19afa28adb924d7
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.129 2008/08/04 18:07:04 gshapiro 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 switch (omode & O_ACCMODE)
704 case O_RDONLY:
705 smode = S_IREAD;
706 break;
708 case O_WRONLY:
709 smode = S_IWRITE;
710 break;
712 case O_RDWR:
713 smode = S_IREAD|S_IWRITE;
714 break;
716 default:
717 smode = 0;
718 break;
720 if (bitset(SFF_OPENASROOT, sff))
721 rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
722 sff, smode, &stb);
723 else
724 rval = safefile(fn, RealUid, RealGid, RealUserName,
725 sff, smode, &stb);
726 if (rval != 0)
728 errno = rval;
729 return -1;
731 if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
732 omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
733 else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
735 /* The file exists so an exclusive create would fail */
736 errno = EEXIST;
737 return -1;
740 #if !NOFTRUNCATE
741 truncate = bitset(O_TRUNC, omode);
742 if (truncate)
743 omode &= ~O_TRUNC;
744 #endif /* !NOFTRUNCATE */
746 fd = dfopen(fn, omode, cmode, sff);
747 if (fd < 0)
748 return fd;
749 if (filechanged(fn, fd, &stb))
751 syserr("554 5.3.0 cannot open: file %s changed after open", fn);
752 (void) close(fd);
753 errno = E_SM_FILECHANGE;
754 return -1;
757 #if !NOFTRUNCATE
758 if (truncate &&
759 ftruncate(fd, (off_t) 0) < 0)
761 int save_errno;
763 save_errno = errno;
764 syserr("554 5.3.0 cannot open: file %s could not be truncated",
765 fn);
766 (void) close(fd);
767 errno = save_errno;
768 return -1;
770 #endif /* !NOFTRUNCATE */
772 return fd;
775 ** SAFEFOPEN -- do a file open with extra checking
777 ** Parameters:
778 ** fn -- the file name to open.
779 ** omode -- the open-style mode flags.
780 ** cmode -- the create-style mode flags.
781 ** sff -- safefile flags.
783 ** Returns:
784 ** Same as fopen.
787 SM_FILE_T *
788 safefopen(fn, omode, cmode, sff)
789 char *fn;
790 int omode;
791 int cmode;
792 long sff;
794 int fd;
795 int save_errno;
796 SM_FILE_T *fp;
797 int fmode;
799 switch (omode & O_ACCMODE)
801 case O_RDONLY:
802 fmode = SM_IO_RDONLY;
803 break;
805 case O_WRONLY:
806 if (bitset(O_APPEND, omode))
807 fmode = SM_IO_APPEND;
808 else
809 fmode = SM_IO_WRONLY;
810 break;
812 case O_RDWR:
813 if (bitset(O_TRUNC, omode))
814 fmode = SM_IO_RDWRTR;
815 else if (bitset(O_APPEND, omode))
816 fmode = SM_IO_APPENDRW;
817 else
818 fmode = SM_IO_RDWR;
819 break;
821 default:
822 syserr("554 5.3.5 safefopen: unknown omode %o", omode);
823 fmode = 0;
825 fd = safeopen(fn, omode, cmode, sff);
826 if (fd < 0)
828 save_errno = errno;
829 if (tTd(44, 10))
830 sm_dprintf("safefopen: safeopen failed: %s\n",
831 sm_errstring(errno));
832 errno = save_errno;
833 return NULL;
835 fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
836 (void *) &fd, fmode, NULL);
837 if (fp != NULL)
838 return fp;
840 save_errno = errno;
841 if (tTd(44, 10))
843 sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
844 fn, fmode, omode, sff, sm_errstring(errno));
846 (void) close(fd);
847 errno = save_errno;
848 return NULL;
851 ** FILECHANGED -- check to see if file changed after being opened
853 ** Parameters:
854 ** fn -- pathname of file to check.
855 ** fd -- file descriptor to check.
856 ** stb -- stat structure from before open.
858 ** Returns:
859 ** true -- if a problem was detected.
860 ** false -- if this file is still the same.
863 bool
864 filechanged(fn, fd, stb)
865 char *fn;
866 int fd;
867 struct stat *stb;
869 struct stat sta;
871 if (stb->st_mode == ST_MODE_NOFILE)
873 # if HASLSTAT && BOGUS_O_EXCL
874 /* only necessary if exclusive open follows symbolic links */
875 if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
876 return true;
877 # else /* HASLSTAT && BOGUS_O_EXCL */
878 return false;
879 # endif /* HASLSTAT && BOGUS_O_EXCL */
881 if (fstat(fd, &sta) < 0)
882 return true;
884 if (sta.st_nlink != stb->st_nlink ||
885 sta.st_dev != stb->st_dev ||
886 sta.st_ino != stb->st_ino ||
887 # if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
888 sta.st_gen != stb->st_gen ||
889 # endif /* HAS_ST_GEN && 0 */
890 sta.st_uid != stb->st_uid ||
891 sta.st_gid != stb->st_gid)
893 if (tTd(44, 8))
895 sm_dprintf("File changed after opening:\n");
896 sm_dprintf(" nlink = %ld/%ld\n",
897 (long) stb->st_nlink, (long) sta.st_nlink);
898 sm_dprintf(" dev = %ld/%ld\n",
899 (long) stb->st_dev, (long) sta.st_dev);
900 sm_dprintf(" ino = %llu/%llu\n",
901 (ULONGLONG_T) stb->st_ino,
902 (ULONGLONG_T) sta.st_ino);
903 # if HAS_ST_GEN
904 sm_dprintf(" gen = %ld/%ld\n",
905 (long) stb->st_gen, (long) sta.st_gen);
906 # endif /* HAS_ST_GEN */
907 sm_dprintf(" uid = %ld/%ld\n",
908 (long) stb->st_uid, (long) sta.st_uid);
909 sm_dprintf(" gid = %ld/%ld\n",
910 (long) stb->st_gid, (long) sta.st_gid);
912 return true;
915 return false;
918 ** DFOPEN -- determined file open
920 ** This routine has the semantics of open, except that it will
921 ** keep trying a few times to make this happen. The idea is that
922 ** on very loaded systems, we may run out of resources (inodes,
923 ** whatever), so this tries to get around it.
927 dfopen(filename, omode, cmode, sff)
928 char *filename;
929 int omode;
930 int cmode;
931 long sff;
933 register int tries;
934 int fd = -1;
935 struct stat st;
937 for (tries = 0; tries < 10; tries++)
939 (void) sleep((unsigned) (10 * tries));
940 errno = 0;
941 fd = open(filename, omode, cmode);
942 if (fd >= 0)
943 break;
944 switch (errno)
946 case ENFILE: /* system file table full */
947 case EINTR: /* interrupted syscall */
948 #ifdef ETXTBSY
949 case ETXTBSY: /* Apollo: net file locked */
950 #endif /* ETXTBSY */
951 continue;
953 break;
955 if (!bitset(SFF_NOLOCK, sff) &&
956 fd >= 0 &&
957 fstat(fd, &st) >= 0 &&
958 S_ISREG(st.st_mode))
960 int locktype;
962 /* lock the file to avoid accidental conflicts */
963 if ((omode & O_ACCMODE) != O_RDONLY)
964 locktype = LOCK_EX;
965 else
966 locktype = LOCK_SH;
967 if (bitset(SFF_NBLOCK, sff))
968 locktype |= LOCK_NB;
970 if (!lockfile(fd, filename, NULL, locktype))
972 int save_errno = errno;
974 (void) close(fd);
975 fd = -1;
976 errno = save_errno;
978 else
979 errno = 0;
981 return fd;