xref: /freebsd-src/contrib/ntp/libntp/lib/isc/unix/file.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
1*a466cc55SCy Schubert /*
2*a466cc55SCy Schubert  * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3*a466cc55SCy Schubert  * Copyright (C) 2000-2002  Internet Software Consortium.
4*a466cc55SCy Schubert  *
5*a466cc55SCy Schubert  * Permission to use, copy, modify, and/or distribute this software for any
6*a466cc55SCy Schubert  * purpose with or without fee is hereby granted, provided that the above
7*a466cc55SCy Schubert  * copyright notice and this permission notice appear in all copies.
8*a466cc55SCy Schubert  *
9*a466cc55SCy Schubert  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10*a466cc55SCy Schubert  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11*a466cc55SCy Schubert  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12*a466cc55SCy Schubert  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13*a466cc55SCy Schubert  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14*a466cc55SCy Schubert  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15*a466cc55SCy Schubert  * PERFORMANCE OF THIS SOFTWARE.
16*a466cc55SCy Schubert  */
17*a466cc55SCy Schubert 
18*a466cc55SCy Schubert /*
19*a466cc55SCy Schubert  * Portions Copyright (c) 1987, 1993
20*a466cc55SCy Schubert  *      The Regents of the University of California.  All rights reserved.
21*a466cc55SCy Schubert  *
22*a466cc55SCy Schubert  * Redistribution and use in source and binary forms, with or without
23*a466cc55SCy Schubert  * modification, are permitted provided that the following conditions
24*a466cc55SCy Schubert  * are met:
25*a466cc55SCy Schubert  * 1. Redistributions of source code must retain the above copyright
26*a466cc55SCy Schubert  *    notice, this list of conditions and the following disclaimer.
27*a466cc55SCy Schubert  * 2. Redistributions in binary form must reproduce the above copyright
28*a466cc55SCy Schubert  *    notice, this list of conditions and the following disclaimer in the
29*a466cc55SCy Schubert  *    documentation and/or other materials provided with the distribution.
30*a466cc55SCy Schubert  * 3. All advertising materials mentioning features or use of this software
31*a466cc55SCy Schubert  *    must display the following acknowledgement:
32*a466cc55SCy Schubert  *      This product includes software developed by the University of
33*a466cc55SCy Schubert  *      California, Berkeley and its contributors.
34*a466cc55SCy Schubert  * 4. Neither the name of the University nor the names of its contributors
35*a466cc55SCy Schubert  *    may be used to endorse or promote products derived from this software
36*a466cc55SCy Schubert  *    without specific prior written permission.
37*a466cc55SCy Schubert  *
38*a466cc55SCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
39*a466cc55SCy Schubert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40*a466cc55SCy Schubert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41*a466cc55SCy Schubert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
42*a466cc55SCy Schubert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43*a466cc55SCy Schubert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44*a466cc55SCy Schubert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45*a466cc55SCy Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46*a466cc55SCy Schubert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47*a466cc55SCy Schubert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48*a466cc55SCy Schubert  * SUCH DAMAGE.
49*a466cc55SCy Schubert  */
50*a466cc55SCy Schubert 
51*a466cc55SCy Schubert /* $Id$ */
52*a466cc55SCy Schubert 
53*a466cc55SCy Schubert /*! \file */
54*a466cc55SCy Schubert 
55*a466cc55SCy Schubert #include <config.h>
56*a466cc55SCy Schubert 
57*a466cc55SCy Schubert #include <errno.h>
58*a466cc55SCy Schubert #include <fcntl.h>
59*a466cc55SCy Schubert #include <limits.h>
60*a466cc55SCy Schubert #include <stdlib.h>
61*a466cc55SCy Schubert #include <time.h>		/* Required for utimes on some platforms. */
62*a466cc55SCy Schubert #include <unistd.h>		/* Required for mkstemp on NetBSD. */
63*a466cc55SCy Schubert 
64*a466cc55SCy Schubert 
65*a466cc55SCy Schubert #include <sys/stat.h>
66*a466cc55SCy Schubert #include <sys/time.h>
67*a466cc55SCy Schubert 
68*a466cc55SCy Schubert #include <isc/dir.h>
69*a466cc55SCy Schubert #include <isc/file.h>
70*a466cc55SCy Schubert #include <isc/log.h>
71*a466cc55SCy Schubert #include <isc/mem.h>
72*a466cc55SCy Schubert #include <isc/random.h>
73*a466cc55SCy Schubert #include <isc/string.h>
74*a466cc55SCy Schubert #include <isc/time.h>
75*a466cc55SCy Schubert #include <isc/util.h>
76*a466cc55SCy Schubert 
77*a466cc55SCy Schubert #include "errno2result.h"
78*a466cc55SCy Schubert #include "ntp_stdlib.h"		/* NTP change for strlcpy, strlcat */
79*a466cc55SCy Schubert 
80*a466cc55SCy Schubert /*
81*a466cc55SCy Schubert  * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
82*a466cc55SCy Schubert  * it might be good to provide a mechanism that allows for the results
83*a466cc55SCy Schubert  * of a previous stat() to be used again without having to do another stat,
84*a466cc55SCy Schubert  * such as perl's mechanism of using "_" in place of a file name to indicate
85*a466cc55SCy Schubert  * that the results of the last stat should be used.  But then you get into
86*a466cc55SCy Schubert  * annoying MP issues.   BTW, Win32 has stat().
87*a466cc55SCy Schubert  */
88*a466cc55SCy Schubert static isc_result_t
file_stats(const char * file,struct stat * stats)89*a466cc55SCy Schubert file_stats(const char *file, struct stat *stats) {
90*a466cc55SCy Schubert 	isc_result_t result = ISC_R_SUCCESS;
91*a466cc55SCy Schubert 
92*a466cc55SCy Schubert 	REQUIRE(file != NULL);
93*a466cc55SCy Schubert 	REQUIRE(stats != NULL);
94*a466cc55SCy Schubert 
95*a466cc55SCy Schubert 	if (stat(file, stats) != 0)
96*a466cc55SCy Schubert 		result = isc__errno2result(errno);
97*a466cc55SCy Schubert 
98*a466cc55SCy Schubert 	return (result);
99*a466cc55SCy Schubert }
100*a466cc55SCy Schubert 
101*a466cc55SCy Schubert isc_result_t
isc_file_getmodtime(const char * file,isc_time_t * itime)102*a466cc55SCy Schubert isc_file_getmodtime(const char *file, isc_time_t *itime) {
103*a466cc55SCy Schubert 	isc_result_t result;
104*a466cc55SCy Schubert 	struct stat stats;
105*a466cc55SCy Schubert 
106*a466cc55SCy Schubert 	REQUIRE(file != NULL);
107*a466cc55SCy Schubert 	REQUIRE(itime != NULL);
108*a466cc55SCy Schubert 
109*a466cc55SCy Schubert 	result = file_stats(file, &stats);
110*a466cc55SCy Schubert 
111*a466cc55SCy Schubert 	if (result == ISC_R_SUCCESS)
112*a466cc55SCy Schubert 		/*
113*a466cc55SCy Schubert 		 * XXXDCL some operating systems provide nanoseconds, too,
114*a466cc55SCy Schubert 		 * such as BSD/OS via st_mtimespec.
115*a466cc55SCy Schubert 		 */
116*a466cc55SCy Schubert 		isc_time_set(itime, stats.st_mtime, 0);
117*a466cc55SCy Schubert 
118*a466cc55SCy Schubert 	return (result);
119*a466cc55SCy Schubert }
120*a466cc55SCy Schubert 
121*a466cc55SCy Schubert isc_result_t
isc_file_settime(const char * file,isc_time_t * itime)122*a466cc55SCy Schubert isc_file_settime(const char *file, isc_time_t *itime) {
123*a466cc55SCy Schubert 	struct timeval times[2];
124*a466cc55SCy Schubert 
125*a466cc55SCy Schubert 	REQUIRE(file != NULL && itime != NULL);
126*a466cc55SCy Schubert 
127*a466cc55SCy Schubert 	/*
128*a466cc55SCy Schubert 	 * tv_sec is at least a 32 bit quantity on all platforms we're
129*a466cc55SCy Schubert 	 * dealing with, but it is signed on most (all?) of them,
130*a466cc55SCy Schubert 	 * so we need to make sure the high bit isn't set.  This unfortunately
131*a466cc55SCy Schubert 	 * loses when either:
132*a466cc55SCy Schubert 	 *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
133*a466cc55SCy Schubert 	 *	and isc_time_seconds > LONG_MAX, or
134*a466cc55SCy Schubert 	 *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
135*a466cc55SCy Schubert 	 *      and isc_time_seconds has at least 33 significant bits.
136*a466cc55SCy Schubert 	 */
137*a466cc55SCy Schubert 	times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(itime);
138*a466cc55SCy Schubert 
139*a466cc55SCy Schubert 	/*
140*a466cc55SCy Schubert 	 * Here is the real check for the high bit being set.
141*a466cc55SCy Schubert 	 */
142*a466cc55SCy Schubert 	if ((times[0].tv_sec &
143*a466cc55SCy Schubert 	     (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
144*a466cc55SCy Schubert 		return (ISC_R_RANGE);
145*a466cc55SCy Schubert 
146*a466cc55SCy Schubert 	/*
147*a466cc55SCy Schubert 	 * isc_time_nanoseconds guarantees a value that divided by 1000 will
148*a466cc55SCy Schubert 	 * fit into the minimum possible size tv_usec field.  Unfortunately,
149*a466cc55SCy Schubert 	 * we don't know what that type is so can't cast directly ... but
150*a466cc55SCy Schubert 	 * we can at least cast to signed so the IRIX compiler shuts up.
151*a466cc55SCy Schubert 	 */
152*a466cc55SCy Schubert 	times[0].tv_usec = times[1].tv_usec =
153*a466cc55SCy Schubert 		(isc_int32_t)(isc_time_nanoseconds(itime) / 1000);
154*a466cc55SCy Schubert 
155*a466cc55SCy Schubert 	if (utimes(file, times) < 0)
156*a466cc55SCy Schubert 		return (isc__errno2result(errno));
157*a466cc55SCy Schubert 
158*a466cc55SCy Schubert 	return (ISC_R_SUCCESS);
159*a466cc55SCy Schubert }
160*a466cc55SCy Schubert 
161*a466cc55SCy Schubert #undef TEMPLATE
162*a466cc55SCy Schubert #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
163*a466cc55SCy Schubert 
164*a466cc55SCy Schubert isc_result_t
isc_file_mktemplate(const char * path,char * buf,size_t buflen)165*a466cc55SCy Schubert isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
166*a466cc55SCy Schubert 	return (isc_file_template(path, TEMPLATE, buf, buflen));
167*a466cc55SCy Schubert }
168*a466cc55SCy Schubert 
169*a466cc55SCy Schubert isc_result_t
isc_file_template(const char * path,const char * templet,char * buf,size_t buflen)170*a466cc55SCy Schubert isc_file_template(const char *path, const char *templet, char *buf,
171*a466cc55SCy Schubert 			size_t buflen) {
172*a466cc55SCy Schubert 	char *s;
173*a466cc55SCy Schubert 
174*a466cc55SCy Schubert 	REQUIRE(path != NULL);
175*a466cc55SCy Schubert 	REQUIRE(templet != NULL);
176*a466cc55SCy Schubert 	REQUIRE(buf != NULL);
177*a466cc55SCy Schubert 
178*a466cc55SCy Schubert 	s = strrchr(templet, '/');
179*a466cc55SCy Schubert 	if (s != NULL)
180*a466cc55SCy Schubert 		templet = s + 1;
181*a466cc55SCy Schubert 
182*a466cc55SCy Schubert 	s = strrchr(path, '/');
183*a466cc55SCy Schubert 
184*a466cc55SCy Schubert 	if (s != NULL) {
185*a466cc55SCy Schubert 		if ((s - path + 1 + strlen(templet) + 1) > buflen)
186*a466cc55SCy Schubert 			return (ISC_R_NOSPACE);
187*a466cc55SCy Schubert 
188*a466cc55SCy Schubert 		strlcpy(buf, path, buflen);
189*a466cc55SCy Schubert 		buf[s - path + 1] = '\0';
190*a466cc55SCy Schubert 		strlcat(buf, templet, buflen);
191*a466cc55SCy Schubert 	} else {
192*a466cc55SCy Schubert 		if ((strlen(templet) + 1) > buflen)
193*a466cc55SCy Schubert 			return (ISC_R_NOSPACE);
194*a466cc55SCy Schubert 
195*a466cc55SCy Schubert 		strlcpy(buf, templet, buflen);
196*a466cc55SCy Schubert 	}
197*a466cc55SCy Schubert 
198*a466cc55SCy Schubert 	return (ISC_R_SUCCESS);
199*a466cc55SCy Schubert }
200*a466cc55SCy Schubert 
201*a466cc55SCy Schubert static char alphnum[] =
202*a466cc55SCy Schubert 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
203*a466cc55SCy Schubert 
204*a466cc55SCy Schubert isc_result_t
isc_file_renameunique(const char * file,char * templet)205*a466cc55SCy Schubert isc_file_renameunique(const char *file, char *templet) {
206*a466cc55SCy Schubert 	char *x;
207*a466cc55SCy Schubert 	char *cp;
208*a466cc55SCy Schubert 	isc_uint32_t which;
209*a466cc55SCy Schubert 
210*a466cc55SCy Schubert 	REQUIRE(file != NULL);
211*a466cc55SCy Schubert 	REQUIRE(templet != NULL);
212*a466cc55SCy Schubert 
213*a466cc55SCy Schubert 	cp = templet;
214*a466cc55SCy Schubert 	while (*cp != '\0')
215*a466cc55SCy Schubert 		cp++;
216*a466cc55SCy Schubert 	if (cp == templet)
217*a466cc55SCy Schubert 		return (ISC_R_FAILURE);
218*a466cc55SCy Schubert 
219*a466cc55SCy Schubert 	x = cp--;
220*a466cc55SCy Schubert 	while (cp >= templet && *cp == 'X') {
221*a466cc55SCy Schubert 		isc_random_get(&which);
222*a466cc55SCy Schubert 		*cp = alphnum[which % (sizeof(alphnum) - 1)];
223*a466cc55SCy Schubert 		x = cp--;
224*a466cc55SCy Schubert 	}
225*a466cc55SCy Schubert 	while (link(file, templet) == -1) {
226*a466cc55SCy Schubert 		if (errno != EEXIST)
227*a466cc55SCy Schubert 			return (isc__errno2result(errno));
228*a466cc55SCy Schubert 		for (cp = x;;) {
229*a466cc55SCy Schubert 			char *t;
230*a466cc55SCy Schubert 			if (*cp == '\0')
231*a466cc55SCy Schubert 				return (ISC_R_FAILURE);
232*a466cc55SCy Schubert 			t = strchr(alphnum, *cp);
233*a466cc55SCy Schubert 			if (t == NULL || *++t == '\0')
234*a466cc55SCy Schubert 				*cp++ = alphnum[0];
235*a466cc55SCy Schubert 			else {
236*a466cc55SCy Schubert 				*cp = *t;
237*a466cc55SCy Schubert 				break;
238*a466cc55SCy Schubert 			}
239*a466cc55SCy Schubert 		}
240*a466cc55SCy Schubert 	}
241*a466cc55SCy Schubert 	if (unlink(file) < 0)
242*a466cc55SCy Schubert 		if (errno != ENOENT)
243*a466cc55SCy Schubert 			return (isc__errno2result(errno));
244*a466cc55SCy Schubert 	return (ISC_R_SUCCESS);
245*a466cc55SCy Schubert }
246*a466cc55SCy Schubert 
247*a466cc55SCy Schubert isc_result_t
isc_file_openunique(char * templet,FILE ** fp)248*a466cc55SCy Schubert isc_file_openunique(char *templet, FILE **fp) {
249*a466cc55SCy Schubert 	int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
250*a466cc55SCy Schubert 	return (isc_file_openuniquemode(templet, mode, fp));
251*a466cc55SCy Schubert }
252*a466cc55SCy Schubert 
253*a466cc55SCy Schubert isc_result_t
isc_file_openuniqueprivate(char * templet,FILE ** fp)254*a466cc55SCy Schubert isc_file_openuniqueprivate(char *templet, FILE **fp) {
255*a466cc55SCy Schubert 	int mode = S_IWUSR|S_IRUSR;
256*a466cc55SCy Schubert 	return (isc_file_openuniquemode(templet, mode, fp));
257*a466cc55SCy Schubert }
258*a466cc55SCy Schubert 
259*a466cc55SCy Schubert isc_result_t
isc_file_openuniquemode(char * templet,int mode,FILE ** fp)260*a466cc55SCy Schubert isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
261*a466cc55SCy Schubert 	int fd;
262*a466cc55SCy Schubert 	FILE *f;
263*a466cc55SCy Schubert 	isc_result_t result = ISC_R_SUCCESS;
264*a466cc55SCy Schubert 	char *x;
265*a466cc55SCy Schubert 	char *cp;
266*a466cc55SCy Schubert 	isc_uint32_t which;
267*a466cc55SCy Schubert 
268*a466cc55SCy Schubert 	REQUIRE(templet != NULL);
269*a466cc55SCy Schubert 	REQUIRE(fp != NULL && *fp == NULL);
270*a466cc55SCy Schubert 
271*a466cc55SCy Schubert 	cp = templet;
272*a466cc55SCy Schubert 	while (*cp != '\0')
273*a466cc55SCy Schubert 		cp++;
274*a466cc55SCy Schubert 	if (cp == templet)
275*a466cc55SCy Schubert 		return (ISC_R_FAILURE);
276*a466cc55SCy Schubert 
277*a466cc55SCy Schubert 	x = cp--;
278*a466cc55SCy Schubert 	while (cp >= templet && *cp == 'X') {
279*a466cc55SCy Schubert 		isc_random_get(&which);
280*a466cc55SCy Schubert 		*cp = alphnum[which % (sizeof(alphnum) - 1)];
281*a466cc55SCy Schubert 		x = cp--;
282*a466cc55SCy Schubert 	}
283*a466cc55SCy Schubert 
284*a466cc55SCy Schubert 
285*a466cc55SCy Schubert 	while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
286*a466cc55SCy Schubert 		if (errno != EEXIST)
287*a466cc55SCy Schubert 			return (isc__errno2result(errno));
288*a466cc55SCy Schubert 		for (cp = x;;) {
289*a466cc55SCy Schubert 			char *t;
290*a466cc55SCy Schubert 			if (*cp == '\0')
291*a466cc55SCy Schubert 				return (ISC_R_FAILURE);
292*a466cc55SCy Schubert 			t = strchr(alphnum, *cp);
293*a466cc55SCy Schubert 			if (t == NULL || *++t == '\0')
294*a466cc55SCy Schubert 				*cp++ = alphnum[0];
295*a466cc55SCy Schubert 			else {
296*a466cc55SCy Schubert 				*cp = *t;
297*a466cc55SCy Schubert 				break;
298*a466cc55SCy Schubert 			}
299*a466cc55SCy Schubert 		}
300*a466cc55SCy Schubert 	}
301*a466cc55SCy Schubert 	f = fdopen(fd, "w+");
302*a466cc55SCy Schubert 	if (f == NULL) {
303*a466cc55SCy Schubert 		result = isc__errno2result(errno);
304*a466cc55SCy Schubert 		if (remove(templet) < 0) {
305*a466cc55SCy Schubert 			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
306*a466cc55SCy Schubert 				      ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
307*a466cc55SCy Schubert 				      "remove '%s': failed", templet);
308*a466cc55SCy Schubert 		}
309*a466cc55SCy Schubert 		(void)close(fd);
310*a466cc55SCy Schubert 	} else
311*a466cc55SCy Schubert 		*fp = f;
312*a466cc55SCy Schubert 
313*a466cc55SCy Schubert 	return (result);
314*a466cc55SCy Schubert }
315*a466cc55SCy Schubert 
316*a466cc55SCy Schubert isc_result_t
isc_file_remove(const char * filename)317*a466cc55SCy Schubert isc_file_remove(const char *filename) {
318*a466cc55SCy Schubert 	int r;
319*a466cc55SCy Schubert 
320*a466cc55SCy Schubert 	REQUIRE(filename != NULL);
321*a466cc55SCy Schubert 
322*a466cc55SCy Schubert 	r = unlink(filename);
323*a466cc55SCy Schubert 	if (r == 0)
324*a466cc55SCy Schubert 		return (ISC_R_SUCCESS);
325*a466cc55SCy Schubert 	else
326*a466cc55SCy Schubert 		return (isc__errno2result(errno));
327*a466cc55SCy Schubert }
328*a466cc55SCy Schubert 
329*a466cc55SCy Schubert isc_result_t
isc_file_rename(const char * oldname,const char * newname)330*a466cc55SCy Schubert isc_file_rename(const char *oldname, const char *newname) {
331*a466cc55SCy Schubert 	int r;
332*a466cc55SCy Schubert 
333*a466cc55SCy Schubert 	REQUIRE(oldname != NULL);
334*a466cc55SCy Schubert 	REQUIRE(newname != NULL);
335*a466cc55SCy Schubert 
336*a466cc55SCy Schubert 	r = rename(oldname, newname);
337*a466cc55SCy Schubert 	if (r == 0)
338*a466cc55SCy Schubert 		return (ISC_R_SUCCESS);
339*a466cc55SCy Schubert 	else
340*a466cc55SCy Schubert 		return (isc__errno2result(errno));
341*a466cc55SCy Schubert }
342*a466cc55SCy Schubert 
343*a466cc55SCy Schubert isc_boolean_t
isc_file_exists(const char * pathname)344*a466cc55SCy Schubert isc_file_exists(const char *pathname) {
345*a466cc55SCy Schubert 	struct stat stats;
346*a466cc55SCy Schubert 
347*a466cc55SCy Schubert 	REQUIRE(pathname != NULL);
348*a466cc55SCy Schubert 
349*a466cc55SCy Schubert 	return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
350*a466cc55SCy Schubert }
351*a466cc55SCy Schubert 
352*a466cc55SCy Schubert isc_result_t
isc_file_isplainfile(const char * filename)353*a466cc55SCy Schubert isc_file_isplainfile(const char *filename) {
354*a466cc55SCy Schubert 	/*
355*a466cc55SCy Schubert 	 * This function returns success if filename is a plain file.
356*a466cc55SCy Schubert 	 */
357*a466cc55SCy Schubert 	struct stat filestat;
358*a466cc55SCy Schubert 	memset(&filestat,0,sizeof(struct stat));
359*a466cc55SCy Schubert 
360*a466cc55SCy Schubert 	if ((stat(filename, &filestat)) == -1)
361*a466cc55SCy Schubert 		return(isc__errno2result(errno));
362*a466cc55SCy Schubert 
363*a466cc55SCy Schubert 	if(! S_ISREG(filestat.st_mode))
364*a466cc55SCy Schubert 		return(ISC_R_INVALIDFILE);
365*a466cc55SCy Schubert 
366*a466cc55SCy Schubert 	return(ISC_R_SUCCESS);
367*a466cc55SCy Schubert }
368*a466cc55SCy Schubert 
369*a466cc55SCy Schubert isc_boolean_t
isc_file_isabsolute(const char * filename)370*a466cc55SCy Schubert isc_file_isabsolute(const char *filename) {
371*a466cc55SCy Schubert 	REQUIRE(filename != NULL);
372*a466cc55SCy Schubert 	return (ISC_TF(filename[0] == '/'));
373*a466cc55SCy Schubert }
374*a466cc55SCy Schubert 
375*a466cc55SCy Schubert isc_boolean_t
isc_file_iscurrentdir(const char * filename)376*a466cc55SCy Schubert isc_file_iscurrentdir(const char *filename) {
377*a466cc55SCy Schubert 	REQUIRE(filename != NULL);
378*a466cc55SCy Schubert 	return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
379*a466cc55SCy Schubert }
380*a466cc55SCy Schubert 
381*a466cc55SCy Schubert isc_boolean_t
isc_file_ischdiridempotent(const char * filename)382*a466cc55SCy Schubert isc_file_ischdiridempotent(const char *filename) {
383*a466cc55SCy Schubert 	REQUIRE(filename != NULL);
384*a466cc55SCy Schubert 	if (isc_file_isabsolute(filename))
385*a466cc55SCy Schubert 		return (ISC_TRUE);
386*a466cc55SCy Schubert 	if (isc_file_iscurrentdir(filename))
387*a466cc55SCy Schubert 		return (ISC_TRUE);
388*a466cc55SCy Schubert 	return (ISC_FALSE);
389*a466cc55SCy Schubert }
390*a466cc55SCy Schubert 
391*a466cc55SCy Schubert const char *
isc_file_basename(const char * filename)392*a466cc55SCy Schubert isc_file_basename(const char *filename) {
393*a466cc55SCy Schubert 	char *s;
394*a466cc55SCy Schubert 
395*a466cc55SCy Schubert 	REQUIRE(filename != NULL);
396*a466cc55SCy Schubert 
397*a466cc55SCy Schubert 	s = strrchr(filename, '/');
398*a466cc55SCy Schubert 	if (s == NULL)
399*a466cc55SCy Schubert 		return (filename);
400*a466cc55SCy Schubert 
401*a466cc55SCy Schubert 	return (s + 1);
402*a466cc55SCy Schubert }
403*a466cc55SCy Schubert 
404*a466cc55SCy Schubert isc_result_t
isc_file_progname(const char * filename,char * buf,size_t buflen)405*a466cc55SCy Schubert isc_file_progname(const char *filename, char *buf, size_t buflen) {
406*a466cc55SCy Schubert 	const char *base;
407*a466cc55SCy Schubert 	size_t len;
408*a466cc55SCy Schubert 
409*a466cc55SCy Schubert 	REQUIRE(filename != NULL);
410*a466cc55SCy Schubert 	REQUIRE(buf != NULL);
411*a466cc55SCy Schubert 
412*a466cc55SCy Schubert 	base = isc_file_basename(filename);
413*a466cc55SCy Schubert 	len = strlen(base) + 1;
414*a466cc55SCy Schubert 
415*a466cc55SCy Schubert 	if (len > buflen)
416*a466cc55SCy Schubert 		return (ISC_R_NOSPACE);
417*a466cc55SCy Schubert 	memcpy(buf, base, len);
418*a466cc55SCy Schubert 
419*a466cc55SCy Schubert 	return (ISC_R_SUCCESS);
420*a466cc55SCy Schubert }
421*a466cc55SCy Schubert 
422*a466cc55SCy Schubert /*
423*a466cc55SCy Schubert  * Put the absolute name of the current directory into 'dirname', which is
424*a466cc55SCy Schubert  * a buffer of at least 'length' characters.  End the string with the
425*a466cc55SCy Schubert  * appropriate path separator, such that the final product could be
426*a466cc55SCy Schubert  * concatenated with a relative pathname to make a valid pathname string.
427*a466cc55SCy Schubert  */
428*a466cc55SCy Schubert static isc_result_t
dir_current(char * dirname,size_t length)429*a466cc55SCy Schubert dir_current(char *dirname, size_t length) {
430*a466cc55SCy Schubert 	char *cwd;
431*a466cc55SCy Schubert 	isc_result_t result = ISC_R_SUCCESS;
432*a466cc55SCy Schubert 
433*a466cc55SCy Schubert 	REQUIRE(dirname != NULL);
434*a466cc55SCy Schubert 	REQUIRE(length > 0U);
435*a466cc55SCy Schubert 
436*a466cc55SCy Schubert 	cwd = getcwd(dirname, length);
437*a466cc55SCy Schubert 
438*a466cc55SCy Schubert 	if (cwd == NULL) {
439*a466cc55SCy Schubert 		if (errno == ERANGE)
440*a466cc55SCy Schubert 			result = ISC_R_NOSPACE;
441*a466cc55SCy Schubert 		else
442*a466cc55SCy Schubert 			result = isc__errno2result(errno);
443*a466cc55SCy Schubert 	} else {
444*a466cc55SCy Schubert 		if (strlen(dirname) + 1 == length)
445*a466cc55SCy Schubert 			result = ISC_R_NOSPACE;
446*a466cc55SCy Schubert 		else if (dirname[1] != '\0')
447*a466cc55SCy Schubert 			strlcat(dirname, "/", length);
448*a466cc55SCy Schubert 	}
449*a466cc55SCy Schubert 
450*a466cc55SCy Schubert 	return (result);
451*a466cc55SCy Schubert }
452*a466cc55SCy Schubert 
453*a466cc55SCy Schubert isc_result_t
isc_file_absolutepath(const char * filename,char * path,size_t pathlen)454*a466cc55SCy Schubert isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
455*a466cc55SCy Schubert 	isc_result_t result;
456*a466cc55SCy Schubert 	result = dir_current(path, pathlen);
457*a466cc55SCy Schubert 	if (result != ISC_R_SUCCESS)
458*a466cc55SCy Schubert 		return (result);
459*a466cc55SCy Schubert 	if (strlen(path) + strlen(filename) + 1 > pathlen)
460*a466cc55SCy Schubert 		return (ISC_R_NOSPACE);
461*a466cc55SCy Schubert 	strlcat(path, filename, pathlen);
462*a466cc55SCy Schubert 	return (ISC_R_SUCCESS);
463*a466cc55SCy Schubert }
464*a466cc55SCy Schubert 
465*a466cc55SCy Schubert isc_result_t
isc_file_truncate(const char * filename,isc_offset_t size)466*a466cc55SCy Schubert isc_file_truncate(const char *filename, isc_offset_t size) {
467*a466cc55SCy Schubert 	isc_result_t result = ISC_R_SUCCESS;
468*a466cc55SCy Schubert 
469*a466cc55SCy Schubert 	if (truncate(filename, size) < 0)
470*a466cc55SCy Schubert 		result = isc__errno2result(errno);
471*a466cc55SCy Schubert 	return (result);
472*a466cc55SCy Schubert }
473*a466cc55SCy Schubert 
474*a466cc55SCy Schubert isc_result_t
isc_file_safecreate(const char * filename,FILE ** fp)475*a466cc55SCy Schubert isc_file_safecreate(const char *filename, FILE **fp) {
476*a466cc55SCy Schubert 	isc_result_t result;
477*a466cc55SCy Schubert 	int flags;
478*a466cc55SCy Schubert 	struct stat sb;
479*a466cc55SCy Schubert 	FILE *f;
480*a466cc55SCy Schubert 	int fd;
481*a466cc55SCy Schubert 
482*a466cc55SCy Schubert 	REQUIRE(filename != NULL);
483*a466cc55SCy Schubert 	REQUIRE(fp != NULL && *fp == NULL);
484*a466cc55SCy Schubert 
485*a466cc55SCy Schubert 	result = file_stats(filename, &sb);
486*a466cc55SCy Schubert 	if (result == ISC_R_SUCCESS) {
487*a466cc55SCy Schubert 		if ((sb.st_mode & S_IFREG) == 0)
488*a466cc55SCy Schubert 			return (ISC_R_INVALIDFILE);
489*a466cc55SCy Schubert 		flags = O_WRONLY | O_TRUNC;
490*a466cc55SCy Schubert 	} else if (result == ISC_R_FILENOTFOUND) {
491*a466cc55SCy Schubert 		flags = O_WRONLY | O_CREAT | O_EXCL;
492*a466cc55SCy Schubert 	} else
493*a466cc55SCy Schubert 		return (result);
494*a466cc55SCy Schubert 
495*a466cc55SCy Schubert 	fd = open(filename, flags, S_IRUSR | S_IWUSR);
496*a466cc55SCy Schubert 	if (fd == -1)
497*a466cc55SCy Schubert 		return (isc__errno2result(errno));
498*a466cc55SCy Schubert 
499*a466cc55SCy Schubert 	f = fdopen(fd, "w");
500*a466cc55SCy Schubert 	if (f == NULL) {
501*a466cc55SCy Schubert 		result = isc__errno2result(errno);
502*a466cc55SCy Schubert 		close(fd);
503*a466cc55SCy Schubert 		return (result);
504*a466cc55SCy Schubert 	}
505*a466cc55SCy Schubert 
506*a466cc55SCy Schubert 	*fp = f;
507*a466cc55SCy Schubert 	return (ISC_R_SUCCESS);
508*a466cc55SCy Schubert }
509*a466cc55SCy Schubert 
510*a466cc55SCy Schubert isc_result_t
isc_file_splitpath(isc_mem_t * mctx,char * path,char ** dirnam,char ** basenam)511*a466cc55SCy Schubert isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirnam, char **basenam)
512*a466cc55SCy Schubert {
513*a466cc55SCy Schubert 	char *dir, *file, *slash;
514*a466cc55SCy Schubert 
515*a466cc55SCy Schubert 	REQUIRE(path != NULL);
516*a466cc55SCy Schubert 
517*a466cc55SCy Schubert 	slash = strrchr(path, '/');
518*a466cc55SCy Schubert 
519*a466cc55SCy Schubert 	if (slash == path) {
520*a466cc55SCy Schubert 		file = ++slash;
521*a466cc55SCy Schubert 		dir = isc_mem_strdup(mctx, "/");
522*a466cc55SCy Schubert 	} else if (slash != NULL) {
523*a466cc55SCy Schubert 		file = ++slash;
524*a466cc55SCy Schubert 		dir = isc_mem_allocate(mctx, slash - path);
525*a466cc55SCy Schubert 		if (dir != NULL)
526*a466cc55SCy Schubert 			strlcpy(dir, path, slash - path);
527*a466cc55SCy Schubert 	} else {
528*a466cc55SCy Schubert 		file = path;
529*a466cc55SCy Schubert 		dir = isc_mem_strdup(mctx, ".");
530*a466cc55SCy Schubert 	}
531*a466cc55SCy Schubert 
532*a466cc55SCy Schubert 	if (dir == NULL)
533*a466cc55SCy Schubert 		return (ISC_R_NOMEMORY);
534*a466cc55SCy Schubert 
535*a466cc55SCy Schubert 	if (*file == '\0') {
536*a466cc55SCy Schubert 		isc_mem_free(mctx, dir);
537*a466cc55SCy Schubert 		return (ISC_R_INVALIDFILE);
538*a466cc55SCy Schubert 	}
539*a466cc55SCy Schubert 
540*a466cc55SCy Schubert 	*dirnam = dir;
541*a466cc55SCy Schubert 	*basenam = file;
542*a466cc55SCy Schubert 
543*a466cc55SCy Schubert 	return (ISC_R_SUCCESS);
544*a466cc55SCy Schubert }
545