1 /*
2 * Copyright (c) 1998-2004 Proofpoint, 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.
7 *
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.
11 *
12 */
13
14 #include <sendmail.h>
15 #include <sm/io.h>
16 #include <sm/errstring.h>
17
18 SM_RCSID("@(#)$Id: safefile.c,v 8.130 2013-11-22 20:51:50 ca Exp $")
19
20
21 /*
22 ** SAFEFILE -- return 0 if a file exists and is safe for a user.
23 **
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 sets).
29 ** flags -- modifiers:
30 ** SFF_MUSTOWN -- "uid" must own this file.
31 ** SFF_NOSLINK -- file cannot be a symbolic link.
32 ** mode -- mode bits that must match.
33 ** st -- if set, points to a stat structure that will
34 ** get the stat info for the file.
35 **
36 ** Returns:
37 ** 0 if fn exists, is owned by uid, and matches mode.
38 ** An errno otherwise. The actual errno is cleared.
39 **
40 ** Side Effects:
41 ** none.
42 */
43
44 int
45 safefile(fn, uid, gid, user, flags, mode, st)
46 char *fn;
47 UID_T uid;
48 GID_T gid;
49 char *user;
50 long flags;
51 int mode;
52 struct stat *st;
53 {
54 register char *p;
55 register struct group *gr = NULL;
56 int file_errno = 0;
57 bool checkpath;
58 struct stat stbuf;
59 struct stat fstbuf;
60 char fbuf[MAXPATHLEN];
61
62 if (tTd(44, 4))
63 sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
64 fn, (int) uid, (int) gid, flags, mode);
65 errno = 0;
66 if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
67 {
68 if (tTd(44, 4))
69 sm_dprintf("\tpathname too long\n");
70 return ENAMETOOLONG;
71 }
72 fn = fbuf;
73 if (st == NULL)
74 st = &fstbuf;
75
76 /* ignore SFF_SAFEDIRPATH if we are debugging */
77 if (RealUid != 0 && RunAsUid == RealUid)
78 flags &= ~SFF_SAFEDIRPATH;
79
80 /* first check to see if the file exists at all */
81 #if HASLSTAT
82 if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
83 : stat(fn, st)) < 0)
84 #else
85 if (stat(fn, st) < 0)
86 #endif
87 {
88 file_errno = errno;
89 }
90 else if (bitset(SFF_SETUIDOK, flags) &&
91 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
92 S_ISREG(st->st_mode))
93 {
94 /*
95 ** If final file is set-user-ID, run as the owner of that
96 ** file. Gotta be careful not to reveal anything too
97 ** soon here!
98 */
99
100 #ifdef SUID_ROOT_FILES_OK
101 if (bitset(S_ISUID, st->st_mode))
102 #else
103 if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
104 st->st_uid != TrustedUid)
105 #endif
106 {
107 uid = st->st_uid;
108 user = NULL;
109 }
110 #ifdef SUID_ROOT_FILES_OK
111 if (bitset(S_ISGID, st->st_mode))
112 #else
113 if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
114 #endif
115 gid = st->st_gid;
116 }
117
118 checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
119 (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
120 if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
121 {
122 int ret;
123
124 /* check the directory */
125 p = strrchr(fn, '/');
126 if (p == NULL)
127 {
128 ret = safedirpath(".", uid, gid, user,
129 flags|SFF_SAFEDIRPATH, 0, 0);
130 }
131 else
132 {
133 *p = '\0';
134 ret = safedirpath(fn, uid, gid, user,
135 flags|SFF_SAFEDIRPATH, 0, 0);
136 *p = '/';
137 }
138 if (ret == 0)
139 {
140 /* directory is safe */
141 checkpath = false;
142 }
143 else
144 {
145 #if HASLSTAT
146 /* Need lstat() information if called stat() before */
147 if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
148 {
149 ret = errno;
150 if (tTd(44, 4))
151 sm_dprintf("\t%s\n", sm_errstring(ret));
152 return ret;
153 }
154 #endif /* HASLSTAT */
155 /* directory is writable: disallow links */
156 flags |= SFF_NOLINK;
157 }
158 }
159
160 if (checkpath)
161 {
162 int ret;
163
164 p = strrchr(fn, '/');
165 if (p == NULL)
166 {
167 ret = safedirpath(".", uid, gid, user, flags, 0, 0);
168 }
169 else
170 {
171 *p = '\0';
172 ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
173 *p = '/';
174 }
175 if (ret != 0)
176 return ret;
177 }
178
179 /*
180 ** If the target file doesn't exist, check the directory to
181 ** ensure that it is writable by this user.
182 */
183
184 if (file_errno != 0)
185 {
186 int ret = file_errno;
187 char *dir = fn;
188
189 if (tTd(44, 4))
190 sm_dprintf("\t%s\n", sm_errstring(ret));
191
192 errno = 0;
193 if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
194 return ret;
195
196 /* check to see if legal to create the file */
197 p = strrchr(dir, '/');
198 if (p == NULL)
199 dir = ".";
200 else if (p == dir)
201 dir = "/";
202 else
203 *p = '\0';
204 if (stat(dir, &stbuf) >= 0)
205 {
206 int md = S_IWRITE|S_IEXEC;
207
208 ret = 0;
209 if (stbuf.st_uid == uid)
210 /* EMPTY */
211 ;
212 else if (uid == 0 && stbuf.st_uid == TrustedUid)
213 /* EMPTY */
214 ;
215 else
216 {
217 md >>= 3;
218 if (stbuf.st_gid == gid)
219 /* EMPTY */
220 ;
221 #ifndef NO_GROUP_SET
222 else if (user != NULL && !DontInitGroups &&
223 ((gr != NULL &&
224 gr->gr_gid == stbuf.st_gid) ||
225 (gr = getgrgid(stbuf.st_gid)) != NULL))
226 {
227 register char **gp;
228
229 for (gp = gr->gr_mem; *gp != NULL; gp++)
230 if (strcmp(*gp, user) == 0)
231 break;
232 if (*gp == NULL)
233 md >>= 3;
234 }
235 #endif /* ! NO_GROUP_SET */
236 else
237 md >>= 3;
238 }
239 if ((stbuf.st_mode & md) != md)
240 ret = errno = EACCES;
241 }
242 else
243 ret = errno;
244 if (tTd(44, 4))
245 sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n",
246 dir, (int) stbuf.st_uid,
247 (unsigned long) stbuf.st_mode,
248 sm_errstring(ret));
249 if (p != NULL)
250 *p = '/';
251 st->st_mode = ST_MODE_NOFILE;
252 return ret;
253 }
254
255 #ifdef S_ISLNK
256 if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
257 {
258 if (tTd(44, 4))
259 sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
260 (unsigned long) st->st_mode);
261 return E_SM_NOSLINK;
262 }
263 #endif /* S_ISLNK */
264 if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
265 {
266 if (tTd(44, 4))
267 sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
268 (unsigned long) st->st_mode);
269 return E_SM_REGONLY;
270 }
271 if (bitset(SFF_NOGWFILES, flags) &&
272 bitset(S_IWGRP, st->st_mode))
273 {
274 if (tTd(44, 4))
275 sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
276 (unsigned long) st->st_mode);
277 return E_SM_GWFILE;
278 }
279 if (bitset(SFF_NOWWFILES, flags) &&
280 bitset(S_IWOTH, st->st_mode))
281 {
282 if (tTd(44, 4))
283 sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
284 (unsigned long) st->st_mode);
285 return E_SM_WWFILE;
286 }
287 if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
288 {
289 if (tTd(44, 4))
290 sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
291 (unsigned long) st->st_mode);
292 return E_SM_GRFILE;
293 }
294 if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
295 {
296 if (tTd(44, 4))
297 sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
298 (unsigned long) st->st_mode);
299 return E_SM_WRFILE;
300 }
301 if (!bitset(SFF_EXECOK, flags) &&
302 bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
303 bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
304 {
305 if (tTd(44, 4))
306 sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n",
307 (unsigned long) st->st_mode);
308 return E_SM_ISEXEC;
309 }
310 if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
311 {
312 if (tTd(44, 4))
313 sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
314 (int) st->st_nlink);
315 return E_SM_NOHLINK;
316 }
317
318 if (uid == 0 && bitset(SFF_OPENASROOT, flags))
319 /* EMPTY */
320 ;
321 else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
322 mode >>= 6;
323 else if (st->st_uid == uid)
324 /* EMPTY */
325 ;
326 else if (uid == 0 && st->st_uid == TrustedUid)
327 /* EMPTY */
328 ;
329 else
330 {
331 mode >>= 3;
332 if (st->st_gid == gid)
333 /* EMPTY */
334 ;
335 #ifndef NO_GROUP_SET
336 else if (user != NULL && !DontInitGroups &&
337 ((gr != NULL && gr->gr_gid == st->st_gid) ||
338 (gr = getgrgid(st->st_gid)) != NULL))
339 {
340 register char **gp;
341
342 for (gp = gr->gr_mem; *gp != NULL; gp++)
343 if (strcmp(*gp, user) == 0)
344 break;
345 if (*gp == NULL)
346 mode >>= 3;
347 }
348 #endif /* ! NO_GROUP_SET */
349 else
350 mode >>= 3;
351 }
352 if (tTd(44, 4))
353 sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
354 (int) st->st_uid, (int) st->st_nlink,
355 (unsigned long) st->st_mode, (unsigned long) mode);
356 if ((st->st_uid == uid || st->st_uid == 0 ||
357 st->st_uid == TrustedUid ||
358 !bitset(SFF_MUSTOWN, flags)) &&
359 (st->st_mode & mode) == mode)
360 {
361 if (tTd(44, 4))
362 sm_dprintf("\tOK\n");
363 return 0;
364 }
365 if (tTd(44, 4))
366 sm_dprintf("\tEACCES\n");
367 return EACCES;
368 }
369 /*
370 ** SAFEDIRPATH -- check to make sure a path to a directory is safe
371 **
372 ** Safe means not writable and owned by the right folks.
373 **
374 ** Parameters:
375 ** fn -- filename to check.
376 ** uid -- user id to compare against.
377 ** gid -- group id to compare against.
378 ** user -- user name to compare against (used for group
379 ** sets).
380 ** flags -- modifiers:
381 ** SFF_ROOTOK -- ok to use root permissions to open.
382 ** SFF_SAFEDIRPATH -- writable directories are considered
383 ** to be fatal errors.
384 ** level -- symlink recursive level.
385 ** offset -- offset into fn to start checking from.
386 **
387 ** Returns:
388 ** 0 -- if the directory path is "safe".
389 ** else -- an error number associated with the path.
390 */
391
392 int
safedirpath(fn,uid,gid,user,flags,level,offset)393 safedirpath(fn, uid, gid, user, flags, level, offset)
394 char *fn;
395 UID_T uid;
396 GID_T gid;
397 char *user;
398 long flags;
399 int level;
400 int offset;
401 {
402 int ret = 0;
403 int mode = S_IWOTH;
404 char save = '\0';
405 char *saveptr = NULL;
406 char *p, *enddir;
407 register struct group *gr = NULL;
408 char s[MAXLINKPATHLEN];
409 struct stat stbuf;
410
411 /* make sure we aren't in a symlink loop */
412 if (level > MAXSYMLINKS)
413 return ELOOP;
414
415 if (level < 0 || offset < 0 || offset > strlen(fn))
416 return EINVAL;
417
418 /* special case root directory */
419 if (*fn == '\0')
420 fn = "/";
421
422 if (tTd(44, 4))
423 sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
424 fn, (long) uid, (long) gid, flags, level, offset);
425
426 if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
427 mode |= S_IWGRP;
428
429 /* Make a modifiable copy of the filename */
430 if (sm_strlcpy(s, fn, sizeof s) >= sizeof s)
431 return EINVAL;
432
433 p = s + offset;
434 while (p != NULL)
435 {
436 /* put back character */
437 if (saveptr != NULL)
438 {
439 *saveptr = save;
440 saveptr = NULL;
441 p++;
442 }
443
444 if (*p == '\0')
445 break;
446
447 p = strchr(p, '/');
448
449 /* Special case for root directory */
450 if (p == s)
451 {
452 save = *(p + 1);
453 saveptr = p + 1;
454 *(p + 1) = '\0';
455 }
456 else if (p != NULL)
457 {
458 save = *p;
459 saveptr = p;
460 *p = '\0';
461 }
462
463 /* Heuristic: . and .. have already been checked */
464 enddir = strrchr(s, '/');
465 if (enddir != NULL &&
466 (strcmp(enddir, "/..") == 0 ||
467 strcmp(enddir, "/.") == 0))
468 continue;
469
470 if (tTd(44, 20))
471 sm_dprintf("\t[dir %s]\n", s);
472
473 #if HASLSTAT
474 ret = lstat(s, &stbuf);
475 #else
476 ret = stat(s, &stbuf);
477 #endif
478 if (ret < 0)
479 {
480 ret = errno;
481 break;
482 }
483
484 #ifdef S_ISLNK
485 /* Follow symlinks */
486 if (S_ISLNK(stbuf.st_mode))
487 {
488 int linklen;
489 char *target;
490 char buf[MAXPATHLEN];
491 char fullbuf[MAXLINKPATHLEN];
492
493 memset(buf, '\0', sizeof buf);
494 linklen = readlink(s, buf, sizeof buf);
495 if (linklen < 0)
496 {
497 ret = errno;
498 break;
499 }
500 if (linklen >= sizeof buf)
501 {
502 /* file name too long for buffer */
503 ret = errno = EINVAL;
504 break;
505 }
506
507 offset = 0;
508 if (*buf == '/')
509 {
510 target = buf;
511
512 /* If path is the same, avoid rechecks */
513 while (s[offset] == buf[offset] &&
514 s[offset] != '\0')
515 offset++;
516
517 if (s[offset] == '\0' && buf[offset] == '\0')
518 {
519 /* strings match, symlink loop */
520 return ELOOP;
521 }
522
523 /* back off from the mismatch */
524 if (offset > 0)
525 offset--;
526
527 /* Make sure we are at a directory break */
528 if (offset > 0 &&
529 s[offset] != '/' &&
530 s[offset] != '\0')
531 {
532 while (buf[offset] != '/' &&
533 offset > 0)
534 offset--;
535 }
536 if (offset > 0 &&
537 s[offset] == '/' &&
538 buf[offset] == '/')
539 {
540 /* Include the trailing slash */
541 offset++;
542 }
543 }
544 else
545 {
546 char *sptr;
547
548 sptr = strrchr(s, '/');
549 if (sptr != NULL)
550 {
551 *sptr = '\0';
552 offset = sptr + 1 - s;
553 if (sm_strlcpyn(fullbuf,
554 sizeof fullbuf, 2,
555 s, "/") >=
556 sizeof fullbuf ||
557 sm_strlcat(fullbuf, buf,
558 sizeof fullbuf) >=
559 sizeof fullbuf)
560 {
561 ret = EINVAL;
562 break;
563 }
564 *sptr = '/';
565 }
566 else
567 {
568 if (sm_strlcpy(fullbuf, buf,
569 sizeof fullbuf) >=
570 sizeof fullbuf)
571 {
572 ret = EINVAL;
573 break;
574 }
575 }
576 target = fullbuf;
577 }
578 ret = safedirpath(target, uid, gid, user, flags,
579 level + 1, offset);
580 if (ret != 0)
581 break;
582
583 /* Don't check permissions on the link file itself */
584 continue;
585 }
586 #endif /* S_ISLNK */
587
588 if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
589 #ifdef S_ISVTX
590 !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
591 bitset(S_ISVTX, stbuf.st_mode)) &&
592 #endif
593 bitset(mode, stbuf.st_mode))
594 {
595 if (tTd(44, 4))
596 sm_dprintf("\t[dir %s] mode %lo ",
597 s, (unsigned long) stbuf.st_mode);
598 if (bitset(SFF_SAFEDIRPATH, flags))
599 {
600 if (bitset(S_IWOTH, stbuf.st_mode))
601 ret = E_SM_WWDIR;
602 else
603 ret = E_SM_GWDIR;
604 if (tTd(44, 4))
605 sm_dprintf("FATAL\n");
606 break;
607 }
608 if (tTd(44, 4))
609 sm_dprintf("WARNING\n");
610 if (Verbose > 1)
611 message("051 WARNING: %s writable directory %s",
612 bitset(S_IWOTH, stbuf.st_mode)
613 ? "World"
614 : "Group",
615 s);
616 }
617 if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
618 {
619 if (bitset(S_IXOTH, stbuf.st_mode))
620 continue;
621 ret = EACCES;
622 break;
623 }
624
625 /*
626 ** Let OS determine access to file if we are not
627 ** running as a privileged user. This allows ACLs
628 ** to work. Also, if opening as root, assume we can
629 ** scan the directory.
630 */
631 if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
632 continue;
633
634 if (stbuf.st_uid == uid &&
635 bitset(S_IXUSR, stbuf.st_mode))
636 continue;
637 if (stbuf.st_gid == gid &&
638 bitset(S_IXGRP, stbuf.st_mode))
639 continue;
640 #ifndef NO_GROUP_SET
641 if (user != NULL && !DontInitGroups &&
642 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
643 (gr = getgrgid(stbuf.st_gid)) != NULL))
644 {
645 register char **gp;
646
647 for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
648 if (strcmp(*gp, user) == 0)
649 break;
650 if (gp != NULL && *gp != NULL &&
651 bitset(S_IXGRP, stbuf.st_mode))
652 continue;
653 }
654 #endif /* ! NO_GROUP_SET */
655 if (!bitset(S_IXOTH, stbuf.st_mode))
656 {
657 ret = EACCES;
658 break;
659 }
660 }
661 if (tTd(44, 4))
662 sm_dprintf("\t[dir %s] %s\n", fn,
663 ret == 0 ? "OK" : sm_errstring(ret));
664 return ret;
665 }
666 /*
667 ** SAFEOPEN -- do a file open with extra checking
668 **
669 ** Parameters:
670 ** fn -- the file name to open.
671 ** omode -- the open-style mode flags.
672 ** cmode -- the create-style mode flags.
673 ** sff -- safefile flags.
674 **
675 ** Returns:
676 ** Same as open.
677 */
678
679 int
safeopen(fn,omode,cmode,sff)680 safeopen(fn, omode, cmode, sff)
681 char *fn;
682 int omode;
683 int cmode;
684 long sff;
685 {
686 #if !NOFTRUNCATE
687 bool truncate;
688 #endif
689 int rval;
690 int fd;
691 int smode;
692 struct stat stb;
693
694 if (tTd(44, 10))
695 sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
696 fn, omode, cmode, sff);
697
698 if (bitset(O_CREAT, omode))
699 sff |= SFF_CREAT;
700 omode &= ~O_CREAT;
701 switch (omode & O_ACCMODE)
702 {
703 case O_RDONLY:
704 smode = S_IREAD;
705 break;
706
707 case O_WRONLY:
708 smode = S_IWRITE;
709 break;
710
711 case O_RDWR:
712 smode = S_IREAD|S_IWRITE;
713 break;
714
715 default:
716 smode = 0;
717 break;
718 }
719 if (bitset(SFF_OPENASROOT, sff))
720 rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
721 sff, smode, &stb);
722 else
723 rval = safefile(fn, RealUid, RealGid, RealUserName,
724 sff, smode, &stb);
725 if (rval != 0)
726 {
727 errno = rval;
728 return -1;
729 }
730 if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
731 omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
732 else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
733 {
734 /* The file exists so an exclusive create would fail */
735 errno = EEXIST;
736 return -1;
737 }
738
739 #if !NOFTRUNCATE
740 truncate = bitset(O_TRUNC, omode);
741 if (truncate)
742 omode &= ~O_TRUNC;
743 #endif
744
745 fd = dfopen(fn, omode, cmode, sff);
746 if (fd < 0)
747 return fd;
748 if (filechanged(fn, fd, &stb))
749 {
750 syserr("554 5.3.0 cannot open: file %s changed after open", fn);
751 (void) close(fd);
752 errno = E_SM_FILECHANGE;
753 return -1;
754 }
755
756 #if !NOFTRUNCATE
757 if (truncate &&
758 ftruncate(fd, (off_t) 0) < 0)
759 {
760 int save_errno;
761
762 save_errno = errno;
763 syserr("554 5.3.0 cannot open: file %s could not be truncated",
764 fn);
765 (void) close(fd);
766 errno = save_errno;
767 return -1;
768 }
769 #endif /* !NOFTRUNCATE */
770
771 return fd;
772 }
773 /*
774 ** SAFEFOPEN -- do a file open with extra checking
775 **
776 ** Parameters:
777 ** fn -- the file name to open.
778 ** omode -- the open-style mode flags.
779 ** cmode -- the create-style mode flags.
780 ** sff -- safefile flags.
781 **
782 ** Returns:
783 ** Same as fopen.
784 */
785
786 SM_FILE_T *
safefopen(fn,omode,cmode,sff)787 safefopen(fn, omode, cmode, sff)
788 char *fn;
789 int omode;
790 int cmode;
791 long sff;
792 {
793 int fd;
794 int save_errno;
795 SM_FILE_T *fp;
796 int fmode;
797
798 switch (omode & O_ACCMODE)
799 {
800 case O_RDONLY:
801 fmode = SM_IO_RDONLY;
802 break;
803
804 case O_WRONLY:
805 if (bitset(O_APPEND, omode))
806 fmode = SM_IO_APPEND;
807 else
808 fmode = SM_IO_WRONLY;
809 break;
810
811 case O_RDWR:
812 if (bitset(O_TRUNC, omode))
813 fmode = SM_IO_RDWRTR;
814 else if (bitset(O_APPEND, omode))
815 fmode = SM_IO_APPENDRW;
816 else
817 fmode = SM_IO_RDWR;
818 break;
819
820 default:
821 syserr("554 5.3.5 safefopen: unknown omode %o", omode);
822 fmode = 0;
823 }
824 fd = safeopen(fn, omode, cmode, sff);
825 if (fd < 0)
826 {
827 save_errno = errno;
828 if (tTd(44, 10))
829 sm_dprintf("safefopen: safeopen failed: %s\n",
830 sm_errstring(errno));
831 errno = save_errno;
832 return NULL;
833 }
834 fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
835 (void *) &fd, fmode, NULL);
836 if (fp != NULL)
837 return fp;
838
839 save_errno = errno;
840 if (tTd(44, 10))
841 {
842 sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
843 fn, fmode, omode, sff, sm_errstring(errno));
844 }
845 (void) close(fd);
846 errno = save_errno;
847 return NULL;
848 }
849 /*
850 ** FILECHANGED -- check to see if file changed after being opened
851 **
852 ** Parameters:
853 ** fn -- pathname of file to check.
854 ** fd -- file descriptor to check.
855 ** stb -- stat structure from before open.
856 **
857 ** Returns:
858 ** true -- if a problem was detected.
859 ** false -- if this file is still the same.
860 */
861
862 bool
filechanged(fn,fd,stb)863 filechanged(fn, fd, stb)
864 char *fn;
865 int fd;
866 struct stat *stb;
867 {
868 struct stat sta;
869
870 if (stb->st_mode == ST_MODE_NOFILE)
871 {
872 #if HASLSTAT && BOGUS_O_EXCL
873 /* only necessary if exclusive open follows symbolic links */
874 if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
875 return true;
876 #else
877 return false;
878 #endif
879 }
880 if (fstat(fd, &sta) < 0)
881 return true;
882
883 if (sta.st_nlink != stb->st_nlink ||
884 sta.st_dev != stb->st_dev ||
885 sta.st_ino != stb->st_ino ||
886 #if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
887 sta.st_gen != stb->st_gen ||
888 #endif
889 sta.st_uid != stb->st_uid ||
890 sta.st_gid != stb->st_gid)
891 {
892 if (tTd(44, 8))
893 {
894 sm_dprintf("File changed after opening:\n");
895 sm_dprintf(" nlink = %ld/%ld\n",
896 (long) stb->st_nlink, (long) sta.st_nlink);
897 sm_dprintf(" dev = %ld/%ld\n",
898 (long) stb->st_dev, (long) sta.st_dev);
899 sm_dprintf(" ino = %llu/%llu\n",
900 (ULONGLONG_T) stb->st_ino,
901 (ULONGLONG_T) sta.st_ino);
902 #if HAS_ST_GEN
903 sm_dprintf(" gen = %ld/%ld\n",
904 (long) stb->st_gen, (long) sta.st_gen);
905 #endif
906 sm_dprintf(" uid = %ld/%ld\n",
907 (long) stb->st_uid, (long) sta.st_uid);
908 sm_dprintf(" gid = %ld/%ld\n",
909 (long) stb->st_gid, (long) sta.st_gid);
910 }
911 return true;
912 }
913
914 return false;
915 }
916 /*
917 ** DFOPEN -- determined file open
918 **
919 ** This routine has the semantics of open, except that it will
920 ** keep trying a few times to make this happen. The idea is that
921 ** on very loaded systems, we may run out of resources (inodes,
922 ** whatever), so this tries to get around it.
923 */
924
925 int
dfopen(filename,omode,cmode,sff)926 dfopen(filename, omode, cmode, sff)
927 char *filename;
928 int omode;
929 int cmode;
930 long sff;
931 {
932 register int tries;
933 int fd = -1;
934 struct stat st;
935
936 for (tries = 0; tries < 10; tries++)
937 {
938 (void) sleep((unsigned) (10 * tries));
939 errno = 0;
940 fd = open(filename, omode, cmode);
941 if (fd >= 0)
942 break;
943 switch (errno)
944 {
945 case ENFILE: /* system file table full */
946 case EINTR: /* interrupted syscall */
947 #ifdef ETXTBSY
948 case ETXTBSY: /* Apollo: net file locked */
949 #endif
950 continue;
951 }
952 break;
953 }
954 if (!bitset(SFF_NOLOCK, sff) &&
955 fd >= 0 &&
956 fstat(fd, &st) >= 0 &&
957 S_ISREG(st.st_mode))
958 {
959 int locktype;
960
961 /* lock the file to avoid accidental conflicts */
962 if ((omode & O_ACCMODE) != O_RDONLY)
963 locktype = LOCK_EX;
964 else
965 locktype = LOCK_SH;
966 if (bitset(SFF_NBLOCK, sff))
967 locktype |= LOCK_NB;
968
969 if (!lockfile(fd, filename, NULL, locktype))
970 {
971 int save_errno = errno;
972
973 (void) close(fd);
974 fd = -1;
975 errno = save_errno;
976 }
977 else
978 errno = 0;
979 }
980 return fd;
981 }
982