xref: /netbsd-src/external/bsd/ntp/dist/libntp/lib/isc/unix/file.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: file.c,v 1.2 2024/08/18 20:47:15 christos Exp $	*/
2897be3a4Schristos 
3897be3a4Schristos /*
4897be3a4Schristos  * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
5897be3a4Schristos  * Copyright (C) 2000-2002  Internet Software Consortium.
6897be3a4Schristos  *
7897be3a4Schristos  * Permission to use, copy, modify, and/or distribute this software for any
8897be3a4Schristos  * purpose with or without fee is hereby granted, provided that the above
9897be3a4Schristos  * copyright notice and this permission notice appear in all copies.
10897be3a4Schristos  *
11897be3a4Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12897be3a4Schristos  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13897be3a4Schristos  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14897be3a4Schristos  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15897be3a4Schristos  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16897be3a4Schristos  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17897be3a4Schristos  * PERFORMANCE OF THIS SOFTWARE.
18897be3a4Schristos  */
19897be3a4Schristos 
20897be3a4Schristos /*
21897be3a4Schristos  * Portions Copyright (c) 1987, 1993
22897be3a4Schristos  *      The Regents of the University of California.  All rights reserved.
23897be3a4Schristos  *
24897be3a4Schristos  * Redistribution and use in source and binary forms, with or without
25897be3a4Schristos  * modification, are permitted provided that the following conditions
26897be3a4Schristos  * are met:
27897be3a4Schristos  * 1. Redistributions of source code must retain the above copyright
28897be3a4Schristos  *    notice, this list of conditions and the following disclaimer.
29897be3a4Schristos  * 2. Redistributions in binary form must reproduce the above copyright
30897be3a4Schristos  *    notice, this list of conditions and the following disclaimer in the
31897be3a4Schristos  *    documentation and/or other materials provided with the distribution.
32897be3a4Schristos  * 3. All advertising materials mentioning features or use of this software
33897be3a4Schristos  *    must display the following acknowledgement:
34897be3a4Schristos  *      This product includes software developed by the University of
35897be3a4Schristos  *      California, Berkeley and its contributors.
36897be3a4Schristos  * 4. Neither the name of the University nor the names of its contributors
37897be3a4Schristos  *    may be used to endorse or promote products derived from this software
38897be3a4Schristos  *    without specific prior written permission.
39897be3a4Schristos  *
40897be3a4Schristos  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
41897be3a4Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42897be3a4Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43897be3a4Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
44897be3a4Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45897be3a4Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46897be3a4Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47897be3a4Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48897be3a4Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49897be3a4Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50897be3a4Schristos  * SUCH DAMAGE.
51897be3a4Schristos  */
52897be3a4Schristos 
53897be3a4Schristos /* Id */
54897be3a4Schristos 
55897be3a4Schristos /*! \file */
56897be3a4Schristos 
57897be3a4Schristos #include <config.h>
58897be3a4Schristos 
59897be3a4Schristos #include <errno.h>
60897be3a4Schristos #include <fcntl.h>
61897be3a4Schristos #include <limits.h>
62897be3a4Schristos #include <stdlib.h>
63897be3a4Schristos #include <time.h>		/* Required for utimes on some platforms. */
64897be3a4Schristos #include <unistd.h>		/* Required for mkstemp on NetBSD. */
65897be3a4Schristos 
66897be3a4Schristos 
67897be3a4Schristos #include <sys/stat.h>
68897be3a4Schristos #include <sys/time.h>
69897be3a4Schristos 
70897be3a4Schristos #include <isc/dir.h>
71897be3a4Schristos #include <isc/file.h>
72897be3a4Schristos #include <isc/log.h>
73897be3a4Schristos #include <isc/mem.h>
74897be3a4Schristos #include <isc/random.h>
75897be3a4Schristos #include <isc/string.h>
76897be3a4Schristos #include <isc/time.h>
77897be3a4Schristos #include <isc/util.h>
78897be3a4Schristos 
79897be3a4Schristos #include "errno2result.h"
80897be3a4Schristos #include "ntp_stdlib.h"		/* NTP change for strlcpy, strlcat */
81897be3a4Schristos 
82897be3a4Schristos /*
83897be3a4Schristos  * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
84897be3a4Schristos  * it might be good to provide a mechanism that allows for the results
85897be3a4Schristos  * of a previous stat() to be used again without having to do another stat,
86897be3a4Schristos  * such as perl's mechanism of using "_" in place of a file name to indicate
87897be3a4Schristos  * that the results of the last stat should be used.  But then you get into
88897be3a4Schristos  * annoying MP issues.   BTW, Win32 has stat().
89897be3a4Schristos  */
90897be3a4Schristos static isc_result_t
91897be3a4Schristos file_stats(const char *file, struct stat *stats) {
92897be3a4Schristos 	isc_result_t result = ISC_R_SUCCESS;
93897be3a4Schristos 
94897be3a4Schristos 	REQUIRE(file != NULL);
95897be3a4Schristos 	REQUIRE(stats != NULL);
96897be3a4Schristos 
97897be3a4Schristos 	if (stat(file, stats) != 0)
98897be3a4Schristos 		result = isc__errno2result(errno);
99897be3a4Schristos 
100897be3a4Schristos 	return (result);
101897be3a4Schristos }
102897be3a4Schristos 
103897be3a4Schristos isc_result_t
104897be3a4Schristos isc_file_getmodtime(const char *file, isc_time_t *itime) {
105897be3a4Schristos 	isc_result_t result;
106897be3a4Schristos 	struct stat stats;
107897be3a4Schristos 
108897be3a4Schristos 	REQUIRE(file != NULL);
109897be3a4Schristos 	REQUIRE(itime != NULL);
110897be3a4Schristos 
111897be3a4Schristos 	result = file_stats(file, &stats);
112897be3a4Schristos 
113897be3a4Schristos 	if (result == ISC_R_SUCCESS)
114897be3a4Schristos 		/*
115897be3a4Schristos 		 * XXXDCL some operating systems provide nanoseconds, too,
116897be3a4Schristos 		 * such as BSD/OS via st_mtimespec.
117897be3a4Schristos 		 */
118897be3a4Schristos 		isc_time_set(itime, stats.st_mtime, 0);
119897be3a4Schristos 
120897be3a4Schristos 	return (result);
121897be3a4Schristos }
122897be3a4Schristos 
123897be3a4Schristos isc_result_t
124897be3a4Schristos isc_file_settime(const char *file, isc_time_t *itime) {
125897be3a4Schristos 	struct timeval times[2];
126897be3a4Schristos 
127897be3a4Schristos 	REQUIRE(file != NULL && itime != NULL);
128897be3a4Schristos 
129897be3a4Schristos 	/*
130897be3a4Schristos 	 * tv_sec is at least a 32 bit quantity on all platforms we're
131897be3a4Schristos 	 * dealing with, but it is signed on most (all?) of them,
132897be3a4Schristos 	 * so we need to make sure the high bit isn't set.  This unfortunately
133897be3a4Schristos 	 * loses when either:
134897be3a4Schristos 	 *   * tv_sec becomes a signed 64 bit integer but long is 32 bits
135897be3a4Schristos 	 *	and isc_time_seconds > LONG_MAX, or
136897be3a4Schristos 	 *   * isc_time_seconds is changed to be > 32 bits but long is 32 bits
137897be3a4Schristos 	 *      and isc_time_seconds has at least 33 significant bits.
138897be3a4Schristos 	 */
139897be3a4Schristos 	times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(itime);
140897be3a4Schristos 
141897be3a4Schristos 	/*
142897be3a4Schristos 	 * Here is the real check for the high bit being set.
143897be3a4Schristos 	 */
144897be3a4Schristos 	if ((times[0].tv_sec &
145897be3a4Schristos 	     (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
146897be3a4Schristos 		return (ISC_R_RANGE);
147897be3a4Schristos 
148897be3a4Schristos 	/*
149897be3a4Schristos 	 * isc_time_nanoseconds guarantees a value that divided by 1000 will
150897be3a4Schristos 	 * fit into the minimum possible size tv_usec field.  Unfortunately,
151897be3a4Schristos 	 * we don't know what that type is so can't cast directly ... but
152897be3a4Schristos 	 * we can at least cast to signed so the IRIX compiler shuts up.
153897be3a4Schristos 	 */
154897be3a4Schristos 	times[0].tv_usec = times[1].tv_usec =
155897be3a4Schristos 		(isc_int32_t)(isc_time_nanoseconds(itime) / 1000);
156897be3a4Schristos 
157897be3a4Schristos 	if (utimes(file, times) < 0)
158897be3a4Schristos 		return (isc__errno2result(errno));
159897be3a4Schristos 
160897be3a4Schristos 	return (ISC_R_SUCCESS);
161897be3a4Schristos }
162897be3a4Schristos 
163897be3a4Schristos #undef TEMPLATE
164897be3a4Schristos #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
165897be3a4Schristos 
166897be3a4Schristos isc_result_t
167897be3a4Schristos isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
168897be3a4Schristos 	return (isc_file_template(path, TEMPLATE, buf, buflen));
169897be3a4Schristos }
170897be3a4Schristos 
171897be3a4Schristos isc_result_t
172897be3a4Schristos isc_file_template(const char *path, const char *templet, char *buf,
173897be3a4Schristos 			size_t buflen) {
174897be3a4Schristos 	char *s;
175897be3a4Schristos 
176897be3a4Schristos 	REQUIRE(path != NULL);
177897be3a4Schristos 	REQUIRE(templet != NULL);
178897be3a4Schristos 	REQUIRE(buf != NULL);
179897be3a4Schristos 
180897be3a4Schristos 	s = strrchr(templet, '/');
181897be3a4Schristos 	if (s != NULL)
182897be3a4Schristos 		templet = s + 1;
183897be3a4Schristos 
184897be3a4Schristos 	s = strrchr(path, '/');
185897be3a4Schristos 
186897be3a4Schristos 	if (s != NULL) {
187897be3a4Schristos 		if ((s - path + 1 + strlen(templet) + 1) > buflen)
188897be3a4Schristos 			return (ISC_R_NOSPACE);
189897be3a4Schristos 
190897be3a4Schristos 		strlcpy(buf, path, buflen);
191897be3a4Schristos 		buf[s - path + 1] = '\0';
192897be3a4Schristos 		strlcat(buf, templet, buflen);
193897be3a4Schristos 	} else {
194897be3a4Schristos 		if ((strlen(templet) + 1) > buflen)
195897be3a4Schristos 			return (ISC_R_NOSPACE);
196897be3a4Schristos 
197897be3a4Schristos 		strlcpy(buf, templet, buflen);
198897be3a4Schristos 	}
199897be3a4Schristos 
200897be3a4Schristos 	return (ISC_R_SUCCESS);
201897be3a4Schristos }
202897be3a4Schristos 
203897be3a4Schristos static char alphnum[] =
204897be3a4Schristos 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
205897be3a4Schristos 
206897be3a4Schristos isc_result_t
207897be3a4Schristos isc_file_renameunique(const char *file, char *templet) {
208897be3a4Schristos 	char *x;
209897be3a4Schristos 	char *cp;
210897be3a4Schristos 	isc_uint32_t which;
211897be3a4Schristos 
212897be3a4Schristos 	REQUIRE(file != NULL);
213897be3a4Schristos 	REQUIRE(templet != NULL);
214897be3a4Schristos 
215897be3a4Schristos 	cp = templet;
216897be3a4Schristos 	while (*cp != '\0')
217897be3a4Schristos 		cp++;
218897be3a4Schristos 	if (cp == templet)
219897be3a4Schristos 		return (ISC_R_FAILURE);
220897be3a4Schristos 
221897be3a4Schristos 	x = cp--;
222897be3a4Schristos 	while (cp >= templet && *cp == 'X') {
223897be3a4Schristos 		isc_random_get(&which);
224897be3a4Schristos 		*cp = alphnum[which % (sizeof(alphnum) - 1)];
225897be3a4Schristos 		x = cp--;
226897be3a4Schristos 	}
227897be3a4Schristos 	while (link(file, templet) == -1) {
228897be3a4Schristos 		if (errno != EEXIST)
229897be3a4Schristos 			return (isc__errno2result(errno));
230897be3a4Schristos 		for (cp = x;;) {
231897be3a4Schristos 			char *t;
232897be3a4Schristos 			if (*cp == '\0')
233897be3a4Schristos 				return (ISC_R_FAILURE);
234897be3a4Schristos 			t = strchr(alphnum, *cp);
235897be3a4Schristos 			if (t == NULL || *++t == '\0')
236897be3a4Schristos 				*cp++ = alphnum[0];
237897be3a4Schristos 			else {
238897be3a4Schristos 				*cp = *t;
239897be3a4Schristos 				break;
240897be3a4Schristos 			}
241897be3a4Schristos 		}
242897be3a4Schristos 	}
243897be3a4Schristos 	if (unlink(file) < 0)
244897be3a4Schristos 		if (errno != ENOENT)
245897be3a4Schristos 			return (isc__errno2result(errno));
246897be3a4Schristos 	return (ISC_R_SUCCESS);
247897be3a4Schristos }
248897be3a4Schristos 
249897be3a4Schristos isc_result_t
250897be3a4Schristos isc_file_openunique(char *templet, FILE **fp) {
251897be3a4Schristos 	int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
252897be3a4Schristos 	return (isc_file_openuniquemode(templet, mode, fp));
253897be3a4Schristos }
254897be3a4Schristos 
255897be3a4Schristos isc_result_t
256897be3a4Schristos isc_file_openuniqueprivate(char *templet, FILE **fp) {
257897be3a4Schristos 	int mode = S_IWUSR|S_IRUSR;
258897be3a4Schristos 	return (isc_file_openuniquemode(templet, mode, fp));
259897be3a4Schristos }
260897be3a4Schristos 
261897be3a4Schristos isc_result_t
262897be3a4Schristos isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
263897be3a4Schristos 	int fd;
264897be3a4Schristos 	FILE *f;
265897be3a4Schristos 	isc_result_t result = ISC_R_SUCCESS;
266897be3a4Schristos 	char *x;
267897be3a4Schristos 	char *cp;
268897be3a4Schristos 	isc_uint32_t which;
269897be3a4Schristos 
270897be3a4Schristos 	REQUIRE(templet != NULL);
271897be3a4Schristos 	REQUIRE(fp != NULL && *fp == NULL);
272897be3a4Schristos 
273897be3a4Schristos 	cp = templet;
274897be3a4Schristos 	while (*cp != '\0')
275897be3a4Schristos 		cp++;
276897be3a4Schristos 	if (cp == templet)
277897be3a4Schristos 		return (ISC_R_FAILURE);
278897be3a4Schristos 
279897be3a4Schristos 	x = cp--;
280897be3a4Schristos 	while (cp >= templet && *cp == 'X') {
281897be3a4Schristos 		isc_random_get(&which);
282897be3a4Schristos 		*cp = alphnum[which % (sizeof(alphnum) - 1)];
283897be3a4Schristos 		x = cp--;
284897be3a4Schristos 	}
285897be3a4Schristos 
286897be3a4Schristos 
287897be3a4Schristos 	while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
288897be3a4Schristos 		if (errno != EEXIST)
289897be3a4Schristos 			return (isc__errno2result(errno));
290897be3a4Schristos 		for (cp = x;;) {
291897be3a4Schristos 			char *t;
292897be3a4Schristos 			if (*cp == '\0')
293897be3a4Schristos 				return (ISC_R_FAILURE);
294897be3a4Schristos 			t = strchr(alphnum, *cp);
295897be3a4Schristos 			if (t == NULL || *++t == '\0')
296897be3a4Schristos 				*cp++ = alphnum[0];
297897be3a4Schristos 			else {
298897be3a4Schristos 				*cp = *t;
299897be3a4Schristos 				break;
300897be3a4Schristos 			}
301897be3a4Schristos 		}
302897be3a4Schristos 	}
303897be3a4Schristos 	f = fdopen(fd, "w+");
304897be3a4Schristos 	if (f == NULL) {
305897be3a4Schristos 		result = isc__errno2result(errno);
306897be3a4Schristos 		if (remove(templet) < 0) {
307897be3a4Schristos 			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
308897be3a4Schristos 				      ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
309897be3a4Schristos 				      "remove '%s': failed", templet);
310897be3a4Schristos 		}
311897be3a4Schristos 		(void)close(fd);
312897be3a4Schristos 	} else
313897be3a4Schristos 		*fp = f;
314897be3a4Schristos 
315897be3a4Schristos 	return (result);
316897be3a4Schristos }
317897be3a4Schristos 
318897be3a4Schristos isc_result_t
319897be3a4Schristos isc_file_remove(const char *filename) {
320897be3a4Schristos 	int r;
321897be3a4Schristos 
322897be3a4Schristos 	REQUIRE(filename != NULL);
323897be3a4Schristos 
324897be3a4Schristos 	r = unlink(filename);
325897be3a4Schristos 	if (r == 0)
326897be3a4Schristos 		return (ISC_R_SUCCESS);
327897be3a4Schristos 	else
328897be3a4Schristos 		return (isc__errno2result(errno));
329897be3a4Schristos }
330897be3a4Schristos 
331897be3a4Schristos isc_result_t
332897be3a4Schristos isc_file_rename(const char *oldname, const char *newname) {
333897be3a4Schristos 	int r;
334897be3a4Schristos 
335897be3a4Schristos 	REQUIRE(oldname != NULL);
336897be3a4Schristos 	REQUIRE(newname != NULL);
337897be3a4Schristos 
338897be3a4Schristos 	r = rename(oldname, newname);
339897be3a4Schristos 	if (r == 0)
340897be3a4Schristos 		return (ISC_R_SUCCESS);
341897be3a4Schristos 	else
342897be3a4Schristos 		return (isc__errno2result(errno));
343897be3a4Schristos }
344897be3a4Schristos 
345897be3a4Schristos isc_boolean_t
346897be3a4Schristos isc_file_exists(const char *pathname) {
347897be3a4Schristos 	struct stat stats;
348897be3a4Schristos 
349897be3a4Schristos 	REQUIRE(pathname != NULL);
350897be3a4Schristos 
351897be3a4Schristos 	return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
352897be3a4Schristos }
353897be3a4Schristos 
354897be3a4Schristos isc_result_t
355897be3a4Schristos isc_file_isplainfile(const char *filename) {
356897be3a4Schristos 	/*
357897be3a4Schristos 	 * This function returns success if filename is a plain file.
358897be3a4Schristos 	 */
359897be3a4Schristos 	struct stat filestat;
360897be3a4Schristos 	memset(&filestat,0,sizeof(struct stat));
361897be3a4Schristos 
362897be3a4Schristos 	if ((stat(filename, &filestat)) == -1)
363897be3a4Schristos 		return(isc__errno2result(errno));
364897be3a4Schristos 
365897be3a4Schristos 	if(! S_ISREG(filestat.st_mode))
366897be3a4Schristos 		return(ISC_R_INVALIDFILE);
367897be3a4Schristos 
368897be3a4Schristos 	return(ISC_R_SUCCESS);
369897be3a4Schristos }
370897be3a4Schristos 
371897be3a4Schristos isc_boolean_t
372897be3a4Schristos isc_file_isabsolute(const char *filename) {
373897be3a4Schristos 	REQUIRE(filename != NULL);
374897be3a4Schristos 	return (ISC_TF(filename[0] == '/'));
375897be3a4Schristos }
376897be3a4Schristos 
377897be3a4Schristos isc_boolean_t
378897be3a4Schristos isc_file_iscurrentdir(const char *filename) {
379897be3a4Schristos 	REQUIRE(filename != NULL);
380897be3a4Schristos 	return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
381897be3a4Schristos }
382897be3a4Schristos 
383897be3a4Schristos isc_boolean_t
384897be3a4Schristos isc_file_ischdiridempotent(const char *filename) {
385897be3a4Schristos 	REQUIRE(filename != NULL);
386897be3a4Schristos 	if (isc_file_isabsolute(filename))
387897be3a4Schristos 		return (ISC_TRUE);
388897be3a4Schristos 	if (isc_file_iscurrentdir(filename))
389897be3a4Schristos 		return (ISC_TRUE);
390897be3a4Schristos 	return (ISC_FALSE);
391897be3a4Schristos }
392897be3a4Schristos 
393897be3a4Schristos const char *
394897be3a4Schristos isc_file_basename(const char *filename) {
395897be3a4Schristos 	char *s;
396897be3a4Schristos 
397897be3a4Schristos 	REQUIRE(filename != NULL);
398897be3a4Schristos 
399897be3a4Schristos 	s = strrchr(filename, '/');
400897be3a4Schristos 	if (s == NULL)
401897be3a4Schristos 		return (filename);
402897be3a4Schristos 
403897be3a4Schristos 	return (s + 1);
404897be3a4Schristos }
405897be3a4Schristos 
406897be3a4Schristos isc_result_t
407897be3a4Schristos isc_file_progname(const char *filename, char *buf, size_t buflen) {
408897be3a4Schristos 	const char *base;
409897be3a4Schristos 	size_t len;
410897be3a4Schristos 
411897be3a4Schristos 	REQUIRE(filename != NULL);
412897be3a4Schristos 	REQUIRE(buf != NULL);
413897be3a4Schristos 
414897be3a4Schristos 	base = isc_file_basename(filename);
415897be3a4Schristos 	len = strlen(base) + 1;
416897be3a4Schristos 
417897be3a4Schristos 	if (len > buflen)
418897be3a4Schristos 		return (ISC_R_NOSPACE);
419897be3a4Schristos 	memcpy(buf, base, len);
420897be3a4Schristos 
421897be3a4Schristos 	return (ISC_R_SUCCESS);
422897be3a4Schristos }
423897be3a4Schristos 
424897be3a4Schristos /*
425897be3a4Schristos  * Put the absolute name of the current directory into 'dirname', which is
426897be3a4Schristos  * a buffer of at least 'length' characters.  End the string with the
427897be3a4Schristos  * appropriate path separator, such that the final product could be
428897be3a4Schristos  * concatenated with a relative pathname to make a valid pathname string.
429897be3a4Schristos  */
430897be3a4Schristos static isc_result_t
431897be3a4Schristos dir_current(char *dirname, size_t length) {
432897be3a4Schristos 	char *cwd;
433897be3a4Schristos 	isc_result_t result = ISC_R_SUCCESS;
434897be3a4Schristos 
435897be3a4Schristos 	REQUIRE(dirname != NULL);
436897be3a4Schristos 	REQUIRE(length > 0U);
437897be3a4Schristos 
438897be3a4Schristos 	cwd = getcwd(dirname, length);
439897be3a4Schristos 
440897be3a4Schristos 	if (cwd == NULL) {
441897be3a4Schristos 		if (errno == ERANGE)
442897be3a4Schristos 			result = ISC_R_NOSPACE;
443897be3a4Schristos 		else
444897be3a4Schristos 			result = isc__errno2result(errno);
445897be3a4Schristos 	} else {
446897be3a4Schristos 		if (strlen(dirname) + 1 == length)
447897be3a4Schristos 			result = ISC_R_NOSPACE;
448897be3a4Schristos 		else if (dirname[1] != '\0')
449897be3a4Schristos 			strlcat(dirname, "/", length);
450897be3a4Schristos 	}
451897be3a4Schristos 
452897be3a4Schristos 	return (result);
453897be3a4Schristos }
454897be3a4Schristos 
455897be3a4Schristos isc_result_t
456897be3a4Schristos isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
457897be3a4Schristos 	isc_result_t result;
458897be3a4Schristos 	result = dir_current(path, pathlen);
459897be3a4Schristos 	if (result != ISC_R_SUCCESS)
460897be3a4Schristos 		return (result);
461897be3a4Schristos 	if (strlen(path) + strlen(filename) + 1 > pathlen)
462897be3a4Schristos 		return (ISC_R_NOSPACE);
463897be3a4Schristos 	strlcat(path, filename, pathlen);
464897be3a4Schristos 	return (ISC_R_SUCCESS);
465897be3a4Schristos }
466897be3a4Schristos 
467897be3a4Schristos isc_result_t
468897be3a4Schristos isc_file_truncate(const char *filename, isc_offset_t size) {
469897be3a4Schristos 	isc_result_t result = ISC_R_SUCCESS;
470897be3a4Schristos 
471897be3a4Schristos 	if (truncate(filename, size) < 0)
472897be3a4Schristos 		result = isc__errno2result(errno);
473897be3a4Schristos 	return (result);
474897be3a4Schristos }
475897be3a4Schristos 
476897be3a4Schristos isc_result_t
477897be3a4Schristos isc_file_safecreate(const char *filename, FILE **fp) {
478897be3a4Schristos 	isc_result_t result;
479897be3a4Schristos 	int flags;
480897be3a4Schristos 	struct stat sb;
481897be3a4Schristos 	FILE *f;
482897be3a4Schristos 	int fd;
483897be3a4Schristos 
484897be3a4Schristos 	REQUIRE(filename != NULL);
485897be3a4Schristos 	REQUIRE(fp != NULL && *fp == NULL);
486897be3a4Schristos 
487897be3a4Schristos 	result = file_stats(filename, &sb);
488897be3a4Schristos 	if (result == ISC_R_SUCCESS) {
489897be3a4Schristos 		if ((sb.st_mode & S_IFREG) == 0)
490897be3a4Schristos 			return (ISC_R_INVALIDFILE);
491897be3a4Schristos 		flags = O_WRONLY | O_TRUNC;
492897be3a4Schristos 	} else if (result == ISC_R_FILENOTFOUND) {
493897be3a4Schristos 		flags = O_WRONLY | O_CREAT | O_EXCL;
494897be3a4Schristos 	} else
495897be3a4Schristos 		return (result);
496897be3a4Schristos 
497897be3a4Schristos 	fd = open(filename, flags, S_IRUSR | S_IWUSR);
498897be3a4Schristos 	if (fd == -1)
499897be3a4Schristos 		return (isc__errno2result(errno));
500897be3a4Schristos 
501897be3a4Schristos 	f = fdopen(fd, "w");
502897be3a4Schristos 	if (f == NULL) {
503897be3a4Schristos 		result = isc__errno2result(errno);
504897be3a4Schristos 		close(fd);
505897be3a4Schristos 		return (result);
506897be3a4Schristos 	}
507897be3a4Schristos 
508897be3a4Schristos 	*fp = f;
509897be3a4Schristos 	return (ISC_R_SUCCESS);
510897be3a4Schristos }
511897be3a4Schristos 
512897be3a4Schristos isc_result_t
513897be3a4Schristos isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirnam, char **basenam)
514897be3a4Schristos {
515897be3a4Schristos 	char *dir, *file, *slash;
516897be3a4Schristos 
517897be3a4Schristos 	REQUIRE(path != NULL);
518897be3a4Schristos 
519897be3a4Schristos 	slash = strrchr(path, '/');
520897be3a4Schristos 
521897be3a4Schristos 	if (slash == path) {
522897be3a4Schristos 		file = ++slash;
523897be3a4Schristos 		dir = isc_mem_strdup(mctx, "/");
524897be3a4Schristos 	} else if (slash != NULL) {
525897be3a4Schristos 		file = ++slash;
526897be3a4Schristos 		dir = isc_mem_allocate(mctx, slash - path);
527897be3a4Schristos 		if (dir != NULL)
528897be3a4Schristos 			strlcpy(dir, path, slash - path);
529897be3a4Schristos 	} else {
530897be3a4Schristos 		file = path;
531897be3a4Schristos 		dir = isc_mem_strdup(mctx, ".");
532897be3a4Schristos 	}
533897be3a4Schristos 
534897be3a4Schristos 	if (dir == NULL)
535897be3a4Schristos 		return (ISC_R_NOMEMORY);
536897be3a4Schristos 
537897be3a4Schristos 	if (*file == '\0') {
538897be3a4Schristos 		isc_mem_free(mctx, dir);
539897be3a4Schristos 		return (ISC_R_INVALIDFILE);
540897be3a4Schristos 	}
541897be3a4Schristos 
542897be3a4Schristos 	*dirnam = dir;
543897be3a4Schristos 	*basenam = file;
544897be3a4Schristos 
545897be3a4Schristos 	return (ISC_R_SUCCESS);
546897be3a4Schristos }
547