1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2007-2009 Dag-Erling Sm��rgrav
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer
12 *    in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31#include <sys/file.h>
32#include <sys/stat.h>
33
34#include <errno.h>
35#include <stdarg.h>
36#include <unistd.h>
37
38#include <libutil.h>
39
40/*
41 * Reliably open and lock a file.
42 *
43 * Please do not modify this code without first reading the revision history
44 * and discussing your changes with <des@freebsd.org>.  Don't be fooled by the
45 * code's apparent simplicity; there would be no need for this function if it
46 * was easy to get right.
47 */
48static int
49vflopenat(int dirfd, const char *path, int flags, va_list ap)
50{
51	int fd, operation, serrno, trunc;
52	struct stat sb, fsb;
53	mode_t mode;
54
55#ifdef O_EXLOCK
56	flags &= ~O_EXLOCK;
57#endif
58
59	mode = 0;
60	if (flags & O_CREAT) {
61		mode = (mode_t)va_arg(ap, int); /* mode_t promoted to int */
62	}
63
64        operation = LOCK_EX;
65        if (flags & O_NONBLOCK)
66                operation |= LOCK_NB;
67
68	trunc = (flags & O_TRUNC);
69	flags &= ~O_TRUNC;
70
71	for (;;) {
72		if ((fd = openat(dirfd, path, flags, mode)) == -1)
73			/* non-existent or no access */
74			return (-1);
75		if (flock(fd, operation) == -1) {
76			/* unsupported or interrupted */
77			serrno = errno;
78			(void)close(fd);
79			errno = serrno;
80			return (-1);
81		}
82		if (fstatat(dirfd, path, &sb, 0) == -1) {
83			/* disappeared from under our feet */
84			(void)close(fd);
85			continue;
86		}
87		if (fstat(fd, &fsb) == -1) {
88			/* can't happen [tm] */
89			serrno = errno;
90			(void)close(fd);
91			errno = serrno;
92			return (-1);
93		}
94		if (sb.st_dev != fsb.st_dev ||
95		    sb.st_ino != fsb.st_ino) {
96			/* changed under our feet */
97			(void)close(fd);
98			continue;
99		}
100		if (trunc && ftruncate(fd, 0) != 0) {
101			/* can't happen [tm] */
102			serrno = errno;
103			(void)close(fd);
104			errno = serrno;
105			return (-1);
106		}
107		/*
108		 * The following change is provided as a specific example to
109		 * avoid.
110		 */
111#if 0
112		if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
113			serrno = errno;
114			(void)close(fd);
115			errno = serrno;
116			return (-1);
117		}
118#endif
119		return (fd);
120	}
121}
122
123int
124flopen(const char *path, int flags, ...)
125{
126	va_list ap;
127	int ret;
128
129	va_start(ap, flags);
130	ret = vflopenat(AT_FDCWD, path, flags, ap);
131	va_end(ap);
132	return (ret);
133}
134
135int
136flopenat(int dirfd, const char *path, int flags, ...)
137{
138	va_list ap;
139	int ret;
140
141	va_start(ap, flags);
142	ret = vflopenat(dirfd, path, flags, ap);
143	va_end(ap);
144	return (ret);
145}
146