1*4afad4b7Schristos /* $NetBSD: file.c,v 1.1 2024/02/18 20:57:57 christos Exp $ */
2*4afad4b7Schristos
3*4afad4b7Schristos /*
4*4afad4b7Schristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5*4afad4b7Schristos *
6*4afad4b7Schristos * SPDX-License-Identifier: MPL-2.0
7*4afad4b7Schristos *
8*4afad4b7Schristos * This Source Code Form is subject to the terms of the Mozilla Public
9*4afad4b7Schristos * License, v. 2.0. If a copy of the MPL was not distributed with this
10*4afad4b7Schristos * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11*4afad4b7Schristos *
12*4afad4b7Schristos * See the COPYRIGHT file distributed with this work for additional
13*4afad4b7Schristos * information regarding copyright ownership.
14*4afad4b7Schristos */
15*4afad4b7Schristos
16*4afad4b7Schristos /*
17*4afad4b7Schristos * Portions Copyright (c) 1987, 1993
18*4afad4b7Schristos * The Regents of the University of California. All rights reserved.
19*4afad4b7Schristos *
20*4afad4b7Schristos * Redistribution and use in source and binary forms, with or without
21*4afad4b7Schristos * modification, are permitted provided that the following conditions
22*4afad4b7Schristos * are met:
23*4afad4b7Schristos * 1. Redistributions of source code must retain the above copyright
24*4afad4b7Schristos * notice, this list of conditions and the following disclaimer.
25*4afad4b7Schristos * 2. Redistributions in binary form must reproduce the above copyright
26*4afad4b7Schristos * notice, this list of conditions and the following disclaimer in the
27*4afad4b7Schristos * documentation and/or other materials provided with the distribution.
28*4afad4b7Schristos * 3. Neither the name of the University nor the names of its contributors
29*4afad4b7Schristos * may be used to endorse or promote products derived from this software
30*4afad4b7Schristos * without specific prior written permission.
31*4afad4b7Schristos *
32*4afad4b7Schristos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33*4afad4b7Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34*4afad4b7Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35*4afad4b7Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36*4afad4b7Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37*4afad4b7Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38*4afad4b7Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39*4afad4b7Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40*4afad4b7Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41*4afad4b7Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42*4afad4b7Schristos * SUCH DAMAGE.
43*4afad4b7Schristos */
44*4afad4b7Schristos
45*4afad4b7Schristos /*! \file */
46*4afad4b7Schristos
47*4afad4b7Schristos #include <errno.h>
48*4afad4b7Schristos #include <fcntl.h>
49*4afad4b7Schristos #include <inttypes.h>
50*4afad4b7Schristos #include <limits.h>
51*4afad4b7Schristos #include <stdbool.h>
52*4afad4b7Schristos #include <stdlib.h>
53*4afad4b7Schristos #include <sys/stat.h>
54*4afad4b7Schristos #include <sys/time.h>
55*4afad4b7Schristos #include <time.h> /* Required for utimes on some platforms. */
56*4afad4b7Schristos #include <unistd.h> /* Required for mkstemp on NetBSD. */
57*4afad4b7Schristos
58*4afad4b7Schristos #ifdef HAVE_SYS_MMAN_H
59*4afad4b7Schristos #include <sys/mman.h>
60*4afad4b7Schristos #endif /* ifdef HAVE_SYS_MMAN_H */
61*4afad4b7Schristos
62*4afad4b7Schristos #include <isc/dir.h>
63*4afad4b7Schristos #include <isc/file.h>
64*4afad4b7Schristos #include <isc/log.h>
65*4afad4b7Schristos #include <isc/md.h>
66*4afad4b7Schristos #include <isc/mem.h>
67*4afad4b7Schristos #include <isc/platform.h>
68*4afad4b7Schristos #include <isc/print.h>
69*4afad4b7Schristos #include <isc/random.h>
70*4afad4b7Schristos #include <isc/string.h>
71*4afad4b7Schristos #include <isc/time.h>
72*4afad4b7Schristos #include <isc/util.h>
73*4afad4b7Schristos
74*4afad4b7Schristos #include "errno2result.h"
75*4afad4b7Schristos
76*4afad4b7Schristos /*
77*4afad4b7Schristos * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
78*4afad4b7Schristos * it might be good to provide a mechanism that allows for the results
79*4afad4b7Schristos * of a previous stat() to be used again without having to do another stat,
80*4afad4b7Schristos * such as perl's mechanism of using "_" in place of a file name to indicate
81*4afad4b7Schristos * that the results of the last stat should be used. But then you get into
82*4afad4b7Schristos * annoying MP issues. BTW, Win32 has stat().
83*4afad4b7Schristos */
84*4afad4b7Schristos static isc_result_t
file_stats(const char * file,struct stat * stats)85*4afad4b7Schristos file_stats(const char *file, struct stat *stats) {
86*4afad4b7Schristos isc_result_t result = ISC_R_SUCCESS;
87*4afad4b7Schristos
88*4afad4b7Schristos REQUIRE(file != NULL);
89*4afad4b7Schristos REQUIRE(stats != NULL);
90*4afad4b7Schristos
91*4afad4b7Schristos if (stat(file, stats) != 0) {
92*4afad4b7Schristos result = isc__errno2result(errno);
93*4afad4b7Schristos }
94*4afad4b7Schristos
95*4afad4b7Schristos return (result);
96*4afad4b7Schristos }
97*4afad4b7Schristos
98*4afad4b7Schristos static isc_result_t
fd_stats(int fd,struct stat * stats)99*4afad4b7Schristos fd_stats(int fd, struct stat *stats) {
100*4afad4b7Schristos isc_result_t result = ISC_R_SUCCESS;
101*4afad4b7Schristos
102*4afad4b7Schristos REQUIRE(stats != NULL);
103*4afad4b7Schristos
104*4afad4b7Schristos if (fstat(fd, stats) != 0) {
105*4afad4b7Schristos result = isc__errno2result(errno);
106*4afad4b7Schristos }
107*4afad4b7Schristos
108*4afad4b7Schristos return (result);
109*4afad4b7Schristos }
110*4afad4b7Schristos
111*4afad4b7Schristos isc_result_t
isc_file_getsizefd(int fd,off_t * size)112*4afad4b7Schristos isc_file_getsizefd(int fd, off_t *size) {
113*4afad4b7Schristos isc_result_t result;
114*4afad4b7Schristos struct stat stats;
115*4afad4b7Schristos
116*4afad4b7Schristos REQUIRE(size != NULL);
117*4afad4b7Schristos
118*4afad4b7Schristos result = fd_stats(fd, &stats);
119*4afad4b7Schristos
120*4afad4b7Schristos if (result == ISC_R_SUCCESS) {
121*4afad4b7Schristos *size = stats.st_size;
122*4afad4b7Schristos }
123*4afad4b7Schristos
124*4afad4b7Schristos return (result);
125*4afad4b7Schristos }
126*4afad4b7Schristos
127*4afad4b7Schristos isc_result_t
isc_file_mode(const char * file,mode_t * modep)128*4afad4b7Schristos isc_file_mode(const char *file, mode_t *modep) {
129*4afad4b7Schristos isc_result_t result;
130*4afad4b7Schristos struct stat stats;
131*4afad4b7Schristos
132*4afad4b7Schristos REQUIRE(modep != NULL);
133*4afad4b7Schristos
134*4afad4b7Schristos result = file_stats(file, &stats);
135*4afad4b7Schristos if (result == ISC_R_SUCCESS) {
136*4afad4b7Schristos *modep = (stats.st_mode & 07777);
137*4afad4b7Schristos }
138*4afad4b7Schristos
139*4afad4b7Schristos return (result);
140*4afad4b7Schristos }
141*4afad4b7Schristos
142*4afad4b7Schristos isc_result_t
isc_file_getmodtime(const char * file,isc_time_t * modtime)143*4afad4b7Schristos isc_file_getmodtime(const char *file, isc_time_t *modtime) {
144*4afad4b7Schristos isc_result_t result;
145*4afad4b7Schristos struct stat stats;
146*4afad4b7Schristos
147*4afad4b7Schristos REQUIRE(file != NULL);
148*4afad4b7Schristos REQUIRE(modtime != NULL);
149*4afad4b7Schristos
150*4afad4b7Schristos result = file_stats(file, &stats);
151*4afad4b7Schristos
152*4afad4b7Schristos if (result == ISC_R_SUCCESS) {
153*4afad4b7Schristos #if defined(HAVE_STAT_NSEC)
154*4afad4b7Schristos isc_time_set(modtime, stats.st_mtime, stats.st_mtim.tv_nsec);
155*4afad4b7Schristos #else /* if defined(HAVE_STAT_NSEC) */
156*4afad4b7Schristos isc_time_set(modtime, stats.st_mtime, 0);
157*4afad4b7Schristos #endif /* if defined(HAVE_STAT_NSEC) */
158*4afad4b7Schristos }
159*4afad4b7Schristos
160*4afad4b7Schristos return (result);
161*4afad4b7Schristos }
162*4afad4b7Schristos
163*4afad4b7Schristos isc_result_t
isc_file_getsize(const char * file,off_t * size)164*4afad4b7Schristos isc_file_getsize(const char *file, off_t *size) {
165*4afad4b7Schristos isc_result_t result;
166*4afad4b7Schristos struct stat stats;
167*4afad4b7Schristos
168*4afad4b7Schristos REQUIRE(file != NULL);
169*4afad4b7Schristos REQUIRE(size != NULL);
170*4afad4b7Schristos
171*4afad4b7Schristos result = file_stats(file, &stats);
172*4afad4b7Schristos
173*4afad4b7Schristos if (result == ISC_R_SUCCESS) {
174*4afad4b7Schristos *size = stats.st_size;
175*4afad4b7Schristos }
176*4afad4b7Schristos
177*4afad4b7Schristos return (result);
178*4afad4b7Schristos }
179*4afad4b7Schristos
180*4afad4b7Schristos isc_result_t
isc_file_settime(const char * file,isc_time_t * when)181*4afad4b7Schristos isc_file_settime(const char *file, isc_time_t *when) {
182*4afad4b7Schristos struct timeval times[2];
183*4afad4b7Schristos
184*4afad4b7Schristos REQUIRE(file != NULL && when != NULL);
185*4afad4b7Schristos
186*4afad4b7Schristos /*
187*4afad4b7Schristos * tv_sec is at least a 32 bit quantity on all platforms we're
188*4afad4b7Schristos * dealing with, but it is signed on most (all?) of them,
189*4afad4b7Schristos * so we need to make sure the high bit isn't set. This unfortunately
190*4afad4b7Schristos * loses when either:
191*4afad4b7Schristos * * tv_sec becomes a signed 64 bit integer but long is 32 bits
192*4afad4b7Schristos * and isc_time_seconds > LONG_MAX, or
193*4afad4b7Schristos * * isc_time_seconds is changed to be > 32 bits but long is 32 bits
194*4afad4b7Schristos * and isc_time_seconds has at least 33 significant bits.
195*4afad4b7Schristos */
196*4afad4b7Schristos times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(when);
197*4afad4b7Schristos
198*4afad4b7Schristos /*
199*4afad4b7Schristos * Here is the real check for the high bit being set.
200*4afad4b7Schristos */
201*4afad4b7Schristos if ((times[0].tv_sec &
202*4afad4b7Schristos (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
203*4afad4b7Schristos {
204*4afad4b7Schristos return (ISC_R_RANGE);
205*4afad4b7Schristos }
206*4afad4b7Schristos
207*4afad4b7Schristos /*
208*4afad4b7Schristos * isc_time_nanoseconds guarantees a value that divided by 1000 will
209*4afad4b7Schristos * fit into the minimum possible size tv_usec field.
210*4afad4b7Schristos */
211*4afad4b7Schristos times[0].tv_usec = times[1].tv_usec =
212*4afad4b7Schristos (int32_t)(isc_time_nanoseconds(when) / 1000);
213*4afad4b7Schristos
214*4afad4b7Schristos if (utimes(file, times) < 0) {
215*4afad4b7Schristos return (isc__errno2result(errno));
216*4afad4b7Schristos }
217*4afad4b7Schristos
218*4afad4b7Schristos return (ISC_R_SUCCESS);
219*4afad4b7Schristos }
220*4afad4b7Schristos
221*4afad4b7Schristos #undef TEMPLATE
222*4afad4b7Schristos #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
223*4afad4b7Schristos
224*4afad4b7Schristos isc_result_t
isc_file_mktemplate(const char * path,char * buf,size_t buflen)225*4afad4b7Schristos isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
226*4afad4b7Schristos return (isc_file_template(path, TEMPLATE, buf, buflen));
227*4afad4b7Schristos }
228*4afad4b7Schristos
229*4afad4b7Schristos isc_result_t
isc_file_template(const char * path,const char * templet,char * buf,size_t buflen)230*4afad4b7Schristos isc_file_template(const char *path, const char *templet, char *buf,
231*4afad4b7Schristos size_t buflen) {
232*4afad4b7Schristos const char *s;
233*4afad4b7Schristos
234*4afad4b7Schristos REQUIRE(templet != NULL);
235*4afad4b7Schristos REQUIRE(buf != NULL);
236*4afad4b7Schristos
237*4afad4b7Schristos if (path == NULL) {
238*4afad4b7Schristos path = "";
239*4afad4b7Schristos }
240*4afad4b7Schristos
241*4afad4b7Schristos s = strrchr(templet, '/');
242*4afad4b7Schristos if (s != NULL) {
243*4afad4b7Schristos templet = s + 1;
244*4afad4b7Schristos }
245*4afad4b7Schristos
246*4afad4b7Schristos s = strrchr(path, '/');
247*4afad4b7Schristos
248*4afad4b7Schristos if (s != NULL) {
249*4afad4b7Schristos size_t prefixlen = s - path + 1;
250*4afad4b7Schristos if ((prefixlen + strlen(templet) + 1) > buflen) {
251*4afad4b7Schristos return (ISC_R_NOSPACE);
252*4afad4b7Schristos }
253*4afad4b7Schristos
254*4afad4b7Schristos /* Copy 'prefixlen' bytes and NUL terminate. */
255*4afad4b7Schristos strlcpy(buf, path, ISC_MIN(prefixlen + 1, buflen));
256*4afad4b7Schristos strlcat(buf, templet, buflen);
257*4afad4b7Schristos } else {
258*4afad4b7Schristos if ((strlen(templet) + 1) > buflen) {
259*4afad4b7Schristos return (ISC_R_NOSPACE);
260*4afad4b7Schristos }
261*4afad4b7Schristos
262*4afad4b7Schristos strlcpy(buf, templet, buflen);
263*4afad4b7Schristos }
264*4afad4b7Schristos
265*4afad4b7Schristos return (ISC_R_SUCCESS);
266*4afad4b7Schristos }
267*4afad4b7Schristos
268*4afad4b7Schristos static const char alphnum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"
269*4afad4b7Schristos "wxyz0123456789";
270*4afad4b7Schristos
271*4afad4b7Schristos isc_result_t
isc_file_renameunique(const char * file,char * templet)272*4afad4b7Schristos isc_file_renameunique(const char *file, char *templet) {
273*4afad4b7Schristos char *x;
274*4afad4b7Schristos char *cp;
275*4afad4b7Schristos
276*4afad4b7Schristos REQUIRE(file != NULL);
277*4afad4b7Schristos REQUIRE(templet != NULL);
278*4afad4b7Schristos
279*4afad4b7Schristos cp = templet;
280*4afad4b7Schristos while (*cp != '\0') {
281*4afad4b7Schristos cp++;
282*4afad4b7Schristos }
283*4afad4b7Schristos if (cp == templet) {
284*4afad4b7Schristos return (ISC_R_FAILURE);
285*4afad4b7Schristos }
286*4afad4b7Schristos
287*4afad4b7Schristos x = cp--;
288*4afad4b7Schristos while (cp >= templet && *cp == 'X') {
289*4afad4b7Schristos *cp = alphnum[isc_random_uniform(sizeof(alphnum) - 1)];
290*4afad4b7Schristos x = cp--;
291*4afad4b7Schristos }
292*4afad4b7Schristos while (link(file, templet) == -1) {
293*4afad4b7Schristos if (errno != EEXIST) {
294*4afad4b7Schristos return (isc__errno2result(errno));
295*4afad4b7Schristos }
296*4afad4b7Schristos for (cp = x;;) {
297*4afad4b7Schristos const char *t;
298*4afad4b7Schristos if (*cp == '\0') {
299*4afad4b7Schristos return (ISC_R_FAILURE);
300*4afad4b7Schristos }
301*4afad4b7Schristos t = strchr(alphnum, *cp);
302*4afad4b7Schristos if (t == NULL || *++t == '\0') {
303*4afad4b7Schristos *cp++ = alphnum[0];
304*4afad4b7Schristos } else {
305*4afad4b7Schristos *cp = *t;
306*4afad4b7Schristos break;
307*4afad4b7Schristos }
308*4afad4b7Schristos }
309*4afad4b7Schristos }
310*4afad4b7Schristos if (unlink(file) < 0) {
311*4afad4b7Schristos if (errno != ENOENT) {
312*4afad4b7Schristos return (isc__errno2result(errno));
313*4afad4b7Schristos }
314*4afad4b7Schristos }
315*4afad4b7Schristos return (ISC_R_SUCCESS);
316*4afad4b7Schristos }
317*4afad4b7Schristos
318*4afad4b7Schristos isc_result_t
isc_file_openunique(char * templet,FILE ** fp)319*4afad4b7Schristos isc_file_openunique(char *templet, FILE **fp) {
320*4afad4b7Schristos int mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
321*4afad4b7Schristos return (isc_file_openuniquemode(templet, mode, fp));
322*4afad4b7Schristos }
323*4afad4b7Schristos
324*4afad4b7Schristos isc_result_t
isc_file_openuniqueprivate(char * templet,FILE ** fp)325*4afad4b7Schristos isc_file_openuniqueprivate(char *templet, FILE **fp) {
326*4afad4b7Schristos int mode = S_IWUSR | S_IRUSR;
327*4afad4b7Schristos return (isc_file_openuniquemode(templet, mode, fp));
328*4afad4b7Schristos }
329*4afad4b7Schristos
330*4afad4b7Schristos isc_result_t
isc_file_openuniquemode(char * templet,int mode,FILE ** fp)331*4afad4b7Schristos isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
332*4afad4b7Schristos int fd;
333*4afad4b7Schristos FILE *f;
334*4afad4b7Schristos isc_result_t result = ISC_R_SUCCESS;
335*4afad4b7Schristos char *x;
336*4afad4b7Schristos char *cp;
337*4afad4b7Schristos
338*4afad4b7Schristos REQUIRE(templet != NULL);
339*4afad4b7Schristos REQUIRE(fp != NULL && *fp == NULL);
340*4afad4b7Schristos
341*4afad4b7Schristos cp = templet;
342*4afad4b7Schristos while (*cp != '\0') {
343*4afad4b7Schristos cp++;
344*4afad4b7Schristos }
345*4afad4b7Schristos if (cp == templet) {
346*4afad4b7Schristos return (ISC_R_FAILURE);
347*4afad4b7Schristos }
348*4afad4b7Schristos
349*4afad4b7Schristos x = cp--;
350*4afad4b7Schristos while (cp >= templet && *cp == 'X') {
351*4afad4b7Schristos *cp = alphnum[isc_random_uniform(sizeof(alphnum) - 1)];
352*4afad4b7Schristos x = cp--;
353*4afad4b7Schristos }
354*4afad4b7Schristos
355*4afad4b7Schristos while ((fd = open(templet, O_RDWR | O_CREAT | O_EXCL, mode)) == -1) {
356*4afad4b7Schristos if (errno != EEXIST) {
357*4afad4b7Schristos return (isc__errno2result(errno));
358*4afad4b7Schristos }
359*4afad4b7Schristos for (cp = x;;) {
360*4afad4b7Schristos char *t;
361*4afad4b7Schristos if (*cp == '\0') {
362*4afad4b7Schristos return (ISC_R_FAILURE);
363*4afad4b7Schristos }
364*4afad4b7Schristos t = strchr(alphnum, *cp);
365*4afad4b7Schristos if (t == NULL || *++t == '\0') {
366*4afad4b7Schristos *cp++ = alphnum[0];
367*4afad4b7Schristos } else {
368*4afad4b7Schristos *cp = *t;
369*4afad4b7Schristos break;
370*4afad4b7Schristos }
371*4afad4b7Schristos }
372*4afad4b7Schristos }
373*4afad4b7Schristos f = fdopen(fd, "w+");
374*4afad4b7Schristos if (f == NULL) {
375*4afad4b7Schristos result = isc__errno2result(errno);
376*4afad4b7Schristos if (remove(templet) < 0) {
377*4afad4b7Schristos isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
378*4afad4b7Schristos ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
379*4afad4b7Schristos "remove '%s': failed", templet);
380*4afad4b7Schristos }
381*4afad4b7Schristos (void)close(fd);
382*4afad4b7Schristos } else {
383*4afad4b7Schristos *fp = f;
384*4afad4b7Schristos }
385*4afad4b7Schristos
386*4afad4b7Schristos return (result);
387*4afad4b7Schristos }
388*4afad4b7Schristos
389*4afad4b7Schristos isc_result_t
isc_file_bopenunique(char * templet,FILE ** fp)390*4afad4b7Schristos isc_file_bopenunique(char *templet, FILE **fp) {
391*4afad4b7Schristos int mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
392*4afad4b7Schristos return (isc_file_openuniquemode(templet, mode, fp));
393*4afad4b7Schristos }
394*4afad4b7Schristos
395*4afad4b7Schristos isc_result_t
isc_file_bopenuniqueprivate(char * templet,FILE ** fp)396*4afad4b7Schristos isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
397*4afad4b7Schristos int mode = S_IWUSR | S_IRUSR;
398*4afad4b7Schristos return (isc_file_openuniquemode(templet, mode, fp));
399*4afad4b7Schristos }
400*4afad4b7Schristos
401*4afad4b7Schristos isc_result_t
isc_file_bopenuniquemode(char * templet,int mode,FILE ** fp)402*4afad4b7Schristos isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
403*4afad4b7Schristos return (isc_file_openuniquemode(templet, mode, fp));
404*4afad4b7Schristos }
405*4afad4b7Schristos
406*4afad4b7Schristos isc_result_t
isc_file_remove(const char * filename)407*4afad4b7Schristos isc_file_remove(const char *filename) {
408*4afad4b7Schristos int r;
409*4afad4b7Schristos
410*4afad4b7Schristos REQUIRE(filename != NULL);
411*4afad4b7Schristos
412*4afad4b7Schristos r = unlink(filename);
413*4afad4b7Schristos if (r == 0) {
414*4afad4b7Schristos return (ISC_R_SUCCESS);
415*4afad4b7Schristos } else {
416*4afad4b7Schristos return (isc__errno2result(errno));
417*4afad4b7Schristos }
418*4afad4b7Schristos }
419*4afad4b7Schristos
420*4afad4b7Schristos isc_result_t
isc_file_rename(const char * oldname,const char * newname)421*4afad4b7Schristos isc_file_rename(const char *oldname, const char *newname) {
422*4afad4b7Schristos int r;
423*4afad4b7Schristos
424*4afad4b7Schristos REQUIRE(oldname != NULL);
425*4afad4b7Schristos REQUIRE(newname != NULL);
426*4afad4b7Schristos
427*4afad4b7Schristos r = rename(oldname, newname);
428*4afad4b7Schristos if (r == 0) {
429*4afad4b7Schristos return (ISC_R_SUCCESS);
430*4afad4b7Schristos } else {
431*4afad4b7Schristos return (isc__errno2result(errno));
432*4afad4b7Schristos }
433*4afad4b7Schristos }
434*4afad4b7Schristos
435*4afad4b7Schristos bool
isc_file_exists(const char * pathname)436*4afad4b7Schristos isc_file_exists(const char *pathname) {
437*4afad4b7Schristos struct stat stats;
438*4afad4b7Schristos
439*4afad4b7Schristos REQUIRE(pathname != NULL);
440*4afad4b7Schristos
441*4afad4b7Schristos return (file_stats(pathname, &stats) == ISC_R_SUCCESS);
442*4afad4b7Schristos }
443*4afad4b7Schristos
444*4afad4b7Schristos isc_result_t
isc_file_isplainfile(const char * filename)445*4afad4b7Schristos isc_file_isplainfile(const char *filename) {
446*4afad4b7Schristos /*
447*4afad4b7Schristos * This function returns success if filename is a plain file.
448*4afad4b7Schristos */
449*4afad4b7Schristos struct stat filestat;
450*4afad4b7Schristos memset(&filestat, 0, sizeof(struct stat));
451*4afad4b7Schristos
452*4afad4b7Schristos if ((stat(filename, &filestat)) == -1) {
453*4afad4b7Schristos return (isc__errno2result(errno));
454*4afad4b7Schristos }
455*4afad4b7Schristos
456*4afad4b7Schristos if (!S_ISREG(filestat.st_mode)) {
457*4afad4b7Schristos return (ISC_R_INVALIDFILE);
458*4afad4b7Schristos }
459*4afad4b7Schristos
460*4afad4b7Schristos return (ISC_R_SUCCESS);
461*4afad4b7Schristos }
462*4afad4b7Schristos
463*4afad4b7Schristos isc_result_t
isc_file_isplainfilefd(int fd)464*4afad4b7Schristos isc_file_isplainfilefd(int fd) {
465*4afad4b7Schristos /*
466*4afad4b7Schristos * This function returns success if filename is a plain file.
467*4afad4b7Schristos */
468*4afad4b7Schristos struct stat filestat;
469*4afad4b7Schristos memset(&filestat, 0, sizeof(struct stat));
470*4afad4b7Schristos
471*4afad4b7Schristos if ((fstat(fd, &filestat)) == -1) {
472*4afad4b7Schristos return (isc__errno2result(errno));
473*4afad4b7Schristos }
474*4afad4b7Schristos
475*4afad4b7Schristos if (!S_ISREG(filestat.st_mode)) {
476*4afad4b7Schristos return (ISC_R_INVALIDFILE);
477*4afad4b7Schristos }
478*4afad4b7Schristos
479*4afad4b7Schristos return (ISC_R_SUCCESS);
480*4afad4b7Schristos }
481*4afad4b7Schristos
482*4afad4b7Schristos isc_result_t
isc_file_isdirectory(const char * filename)483*4afad4b7Schristos isc_file_isdirectory(const char *filename) {
484*4afad4b7Schristos /*
485*4afad4b7Schristos * This function returns success if filename exists and is a
486*4afad4b7Schristos * directory.
487*4afad4b7Schristos */
488*4afad4b7Schristos struct stat filestat;
489*4afad4b7Schristos memset(&filestat, 0, sizeof(struct stat));
490*4afad4b7Schristos
491*4afad4b7Schristos if ((stat(filename, &filestat)) == -1) {
492*4afad4b7Schristos return (isc__errno2result(errno));
493*4afad4b7Schristos }
494*4afad4b7Schristos
495*4afad4b7Schristos if (!S_ISDIR(filestat.st_mode)) {
496*4afad4b7Schristos return (ISC_R_INVALIDFILE);
497*4afad4b7Schristos }
498*4afad4b7Schristos
499*4afad4b7Schristos return (ISC_R_SUCCESS);
500*4afad4b7Schristos }
501*4afad4b7Schristos
502*4afad4b7Schristos bool
isc_file_isabsolute(const char * filename)503*4afad4b7Schristos isc_file_isabsolute(const char *filename) {
504*4afad4b7Schristos REQUIRE(filename != NULL);
505*4afad4b7Schristos return (filename[0] == '/');
506*4afad4b7Schristos }
507*4afad4b7Schristos
508*4afad4b7Schristos bool
isc_file_iscurrentdir(const char * filename)509*4afad4b7Schristos isc_file_iscurrentdir(const char *filename) {
510*4afad4b7Schristos REQUIRE(filename != NULL);
511*4afad4b7Schristos return (filename[0] == '.' && filename[1] == '\0');
512*4afad4b7Schristos }
513*4afad4b7Schristos
514*4afad4b7Schristos bool
isc_file_ischdiridempotent(const char * filename)515*4afad4b7Schristos isc_file_ischdiridempotent(const char *filename) {
516*4afad4b7Schristos REQUIRE(filename != NULL);
517*4afad4b7Schristos if (isc_file_isabsolute(filename)) {
518*4afad4b7Schristos return (true);
519*4afad4b7Schristos }
520*4afad4b7Schristos if (isc_file_iscurrentdir(filename)) {
521*4afad4b7Schristos return (true);
522*4afad4b7Schristos }
523*4afad4b7Schristos return (false);
524*4afad4b7Schristos }
525*4afad4b7Schristos
526*4afad4b7Schristos const char *
isc_file_basename(const char * filename)527*4afad4b7Schristos isc_file_basename(const char *filename) {
528*4afad4b7Schristos const char *s;
529*4afad4b7Schristos
530*4afad4b7Schristos REQUIRE(filename != NULL);
531*4afad4b7Schristos
532*4afad4b7Schristos s = strrchr(filename, '/');
533*4afad4b7Schristos if (s == NULL) {
534*4afad4b7Schristos return (filename);
535*4afad4b7Schristos }
536*4afad4b7Schristos
537*4afad4b7Schristos return (s + 1);
538*4afad4b7Schristos }
539*4afad4b7Schristos
540*4afad4b7Schristos isc_result_t
isc_file_progname(const char * filename,char * buf,size_t buflen)541*4afad4b7Schristos isc_file_progname(const char *filename, char *buf, size_t buflen) {
542*4afad4b7Schristos const char *base;
543*4afad4b7Schristos size_t len;
544*4afad4b7Schristos
545*4afad4b7Schristos REQUIRE(filename != NULL);
546*4afad4b7Schristos REQUIRE(buf != NULL);
547*4afad4b7Schristos
548*4afad4b7Schristos base = isc_file_basename(filename);
549*4afad4b7Schristos len = strlen(base) + 1;
550*4afad4b7Schristos
551*4afad4b7Schristos if (len > buflen) {
552*4afad4b7Schristos return (ISC_R_NOSPACE);
553*4afad4b7Schristos }
554*4afad4b7Schristos memmove(buf, base, len);
555*4afad4b7Schristos
556*4afad4b7Schristos return (ISC_R_SUCCESS);
557*4afad4b7Schristos }
558*4afad4b7Schristos
559*4afad4b7Schristos /*
560*4afad4b7Schristos * Put the absolute name of the current directory into 'dirname', which is
561*4afad4b7Schristos * a buffer of at least 'length' characters. End the string with the
562*4afad4b7Schristos * appropriate path separator, such that the final product could be
563*4afad4b7Schristos * concatenated with a relative pathname to make a valid pathname string.
564*4afad4b7Schristos */
565*4afad4b7Schristos static isc_result_t
dir_current(char * dirname,size_t length)566*4afad4b7Schristos dir_current(char *dirname, size_t length) {
567*4afad4b7Schristos char *cwd;
568*4afad4b7Schristos isc_result_t result = ISC_R_SUCCESS;
569*4afad4b7Schristos
570*4afad4b7Schristos REQUIRE(dirname != NULL);
571*4afad4b7Schristos REQUIRE(length > 0U);
572*4afad4b7Schristos
573*4afad4b7Schristos cwd = getcwd(dirname, length);
574*4afad4b7Schristos
575*4afad4b7Schristos if (cwd == NULL) {
576*4afad4b7Schristos if (errno == ERANGE) {
577*4afad4b7Schristos result = ISC_R_NOSPACE;
578*4afad4b7Schristos } else {
579*4afad4b7Schristos result = isc__errno2result(errno);
580*4afad4b7Schristos }
581*4afad4b7Schristos } else {
582*4afad4b7Schristos if (strlen(dirname) + 1 == length) {
583*4afad4b7Schristos result = ISC_R_NOSPACE;
584*4afad4b7Schristos } else if (dirname[1] != '\0') {
585*4afad4b7Schristos strlcat(dirname, "/", length);
586*4afad4b7Schristos }
587*4afad4b7Schristos }
588*4afad4b7Schristos
589*4afad4b7Schristos return (result);
590*4afad4b7Schristos }
591*4afad4b7Schristos
592*4afad4b7Schristos isc_result_t
isc_file_absolutepath(const char * filename,char * path,size_t pathlen)593*4afad4b7Schristos isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
594*4afad4b7Schristos isc_result_t result;
595*4afad4b7Schristos result = dir_current(path, pathlen);
596*4afad4b7Schristos if (result != ISC_R_SUCCESS) {
597*4afad4b7Schristos return (result);
598*4afad4b7Schristos }
599*4afad4b7Schristos if (strlen(path) + strlen(filename) + 1 > pathlen) {
600*4afad4b7Schristos return (ISC_R_NOSPACE);
601*4afad4b7Schristos }
602*4afad4b7Schristos strlcat(path, filename, pathlen);
603*4afad4b7Schristos return (ISC_R_SUCCESS);
604*4afad4b7Schristos }
605*4afad4b7Schristos
606*4afad4b7Schristos isc_result_t
isc_file_truncate(const char * filename,isc_offset_t size)607*4afad4b7Schristos isc_file_truncate(const char *filename, isc_offset_t size) {
608*4afad4b7Schristos isc_result_t result = ISC_R_SUCCESS;
609*4afad4b7Schristos
610*4afad4b7Schristos if (truncate(filename, size) < 0) {
611*4afad4b7Schristos result = isc__errno2result(errno);
612*4afad4b7Schristos }
613*4afad4b7Schristos return (result);
614*4afad4b7Schristos }
615*4afad4b7Schristos
616*4afad4b7Schristos isc_result_t
isc_file_safecreate(const char * filename,FILE ** fp)617*4afad4b7Schristos isc_file_safecreate(const char *filename, FILE **fp) {
618*4afad4b7Schristos isc_result_t result;
619*4afad4b7Schristos int flags;
620*4afad4b7Schristos struct stat sb;
621*4afad4b7Schristos FILE *f;
622*4afad4b7Schristos int fd;
623*4afad4b7Schristos
624*4afad4b7Schristos REQUIRE(filename != NULL);
625*4afad4b7Schristos REQUIRE(fp != NULL && *fp == NULL);
626*4afad4b7Schristos
627*4afad4b7Schristos result = file_stats(filename, &sb);
628*4afad4b7Schristos if (result == ISC_R_SUCCESS) {
629*4afad4b7Schristos if ((sb.st_mode & S_IFREG) == 0) {
630*4afad4b7Schristos return (ISC_R_INVALIDFILE);
631*4afad4b7Schristos }
632*4afad4b7Schristos flags = O_WRONLY | O_TRUNC;
633*4afad4b7Schristos } else if (result == ISC_R_FILENOTFOUND) {
634*4afad4b7Schristos flags = O_WRONLY | O_CREAT | O_EXCL;
635*4afad4b7Schristos } else {
636*4afad4b7Schristos return (result);
637*4afad4b7Schristos }
638*4afad4b7Schristos
639*4afad4b7Schristos fd = open(filename, flags, S_IRUSR | S_IWUSR);
640*4afad4b7Schristos if (fd == -1) {
641*4afad4b7Schristos return (isc__errno2result(errno));
642*4afad4b7Schristos }
643*4afad4b7Schristos
644*4afad4b7Schristos f = fdopen(fd, "w");
645*4afad4b7Schristos if (f == NULL) {
646*4afad4b7Schristos result = isc__errno2result(errno);
647*4afad4b7Schristos close(fd);
648*4afad4b7Schristos return (result);
649*4afad4b7Schristos }
650*4afad4b7Schristos
651*4afad4b7Schristos *fp = f;
652*4afad4b7Schristos return (ISC_R_SUCCESS);
653*4afad4b7Schristos }
654*4afad4b7Schristos
655*4afad4b7Schristos isc_result_t
isc_file_splitpath(isc_mem_t * mctx,const char * path,char ** dirname,char const ** bname)656*4afad4b7Schristos isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname,
657*4afad4b7Schristos char const **bname) {
658*4afad4b7Schristos char *dir;
659*4afad4b7Schristos const char *file, *slash;
660*4afad4b7Schristos
661*4afad4b7Schristos if (path == NULL) {
662*4afad4b7Schristos return (ISC_R_INVALIDFILE);
663*4afad4b7Schristos }
664*4afad4b7Schristos
665*4afad4b7Schristos slash = strrchr(path, '/');
666*4afad4b7Schristos
667*4afad4b7Schristos if (slash == path) {
668*4afad4b7Schristos file = ++slash;
669*4afad4b7Schristos dir = isc_mem_strdup(mctx, "/");
670*4afad4b7Schristos } else if (slash != NULL) {
671*4afad4b7Schristos file = ++slash;
672*4afad4b7Schristos dir = isc_mem_allocate(mctx, slash - path);
673*4afad4b7Schristos strlcpy(dir, path, slash - path);
674*4afad4b7Schristos } else {
675*4afad4b7Schristos file = path;
676*4afad4b7Schristos dir = isc_mem_strdup(mctx, ".");
677*4afad4b7Schristos }
678*4afad4b7Schristos
679*4afad4b7Schristos if (dir == NULL) {
680*4afad4b7Schristos return (ISC_R_NOMEMORY);
681*4afad4b7Schristos }
682*4afad4b7Schristos
683*4afad4b7Schristos if (*file == '\0') {
684*4afad4b7Schristos isc_mem_free(mctx, dir);
685*4afad4b7Schristos return (ISC_R_INVALIDFILE);
686*4afad4b7Schristos }
687*4afad4b7Schristos
688*4afad4b7Schristos *dirname = dir;
689*4afad4b7Schristos *bname = file;
690*4afad4b7Schristos
691*4afad4b7Schristos return (ISC_R_SUCCESS);
692*4afad4b7Schristos }
693*4afad4b7Schristos
694*4afad4b7Schristos void *
isc_file_mmap(void * addr,size_t len,int prot,int flags,int fd,off_t offset)695*4afad4b7Schristos isc_file_mmap(void *addr, size_t len, int prot, int flags, int fd,
696*4afad4b7Schristos off_t offset) {
697*4afad4b7Schristos #ifdef HAVE_MMAP
698*4afad4b7Schristos return (mmap(addr, len, prot, flags, fd, offset));
699*4afad4b7Schristos #else /* ifdef HAVE_MMAP */
700*4afad4b7Schristos void *buf;
701*4afad4b7Schristos ssize_t ret;
702*4afad4b7Schristos off_t end;
703*4afad4b7Schristos
704*4afad4b7Schristos UNUSED(addr);
705*4afad4b7Schristos UNUSED(prot);
706*4afad4b7Schristos UNUSED(flags);
707*4afad4b7Schristos
708*4afad4b7Schristos end = lseek(fd, 0, SEEK_END);
709*4afad4b7Schristos lseek(fd, offset, SEEK_SET);
710*4afad4b7Schristos if (end - offset < (off_t)len) {
711*4afad4b7Schristos len = end - offset;
712*4afad4b7Schristos }
713*4afad4b7Schristos
714*4afad4b7Schristos buf = malloc(len);
715*4afad4b7Schristos if (buf == NULL) {
716*4afad4b7Schristos return (NULL);
717*4afad4b7Schristos }
718*4afad4b7Schristos
719*4afad4b7Schristos ret = read(fd, buf, len);
720*4afad4b7Schristos if (ret != (ssize_t)len) {
721*4afad4b7Schristos free(buf);
722*4afad4b7Schristos buf = NULL;
723*4afad4b7Schristos }
724*4afad4b7Schristos
725*4afad4b7Schristos return (buf);
726*4afad4b7Schristos #endif /* ifdef HAVE_MMAP */
727*4afad4b7Schristos }
728*4afad4b7Schristos
729*4afad4b7Schristos int
isc_file_munmap(void * addr,size_t len)730*4afad4b7Schristos isc_file_munmap(void *addr, size_t len) {
731*4afad4b7Schristos #ifdef HAVE_MMAP
732*4afad4b7Schristos return (munmap(addr, len));
733*4afad4b7Schristos #else /* ifdef HAVE_MMAP */
734*4afad4b7Schristos UNUSED(len);
735*4afad4b7Schristos
736*4afad4b7Schristos free(addr);
737*4afad4b7Schristos return (0);
738*4afad4b7Schristos #endif /* ifdef HAVE_MMAP */
739*4afad4b7Schristos }
740*4afad4b7Schristos
741*4afad4b7Schristos #define DISALLOW "\\/ABCDEFGHIJKLMNOPQRSTUVWXYZ"
742*4afad4b7Schristos
743*4afad4b7Schristos static isc_result_t
digest2hex(unsigned char * digest,unsigned int digestlen,char * hash,size_t hashlen)744*4afad4b7Schristos digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
745*4afad4b7Schristos size_t hashlen) {
746*4afad4b7Schristos unsigned int i;
747*4afad4b7Schristos int ret;
748*4afad4b7Schristos for (i = 0; i < digestlen; i++) {
749*4afad4b7Schristos size_t left = hashlen - i * 2;
750*4afad4b7Schristos ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
751*4afad4b7Schristos if (ret < 0 || (size_t)ret >= left) {
752*4afad4b7Schristos return (ISC_R_NOSPACE);
753*4afad4b7Schristos }
754*4afad4b7Schristos }
755*4afad4b7Schristos return (ISC_R_SUCCESS);
756*4afad4b7Schristos }
757*4afad4b7Schristos
758*4afad4b7Schristos isc_result_t
isc_file_sanitize(const char * dir,const char * base,const char * ext,char * path,size_t length)759*4afad4b7Schristos isc_file_sanitize(const char *dir, const char *base, const char *ext,
760*4afad4b7Schristos char *path, size_t length) {
761*4afad4b7Schristos char buf[PATH_MAX];
762*4afad4b7Schristos unsigned char digest[ISC_MAX_MD_SIZE];
763*4afad4b7Schristos unsigned int digestlen;
764*4afad4b7Schristos char hash[ISC_MAX_MD_SIZE * 2 + 1];
765*4afad4b7Schristos size_t l = 0;
766*4afad4b7Schristos isc_result_t err;
767*4afad4b7Schristos
768*4afad4b7Schristos REQUIRE(base != NULL);
769*4afad4b7Schristos REQUIRE(path != NULL);
770*4afad4b7Schristos
771*4afad4b7Schristos l = strlen(base) + 1;
772*4afad4b7Schristos
773*4afad4b7Schristos /*
774*4afad4b7Schristos * allow room for a full sha256 hash (64 chars
775*4afad4b7Schristos * plus null terminator)
776*4afad4b7Schristos */
777*4afad4b7Schristos if (l < 65U) {
778*4afad4b7Schristos l = 65;
779*4afad4b7Schristos }
780*4afad4b7Schristos
781*4afad4b7Schristos if (dir != NULL) {
782*4afad4b7Schristos l += strlen(dir) + 1;
783*4afad4b7Schristos }
784*4afad4b7Schristos if (ext != NULL) {
785*4afad4b7Schristos l += strlen(ext) + 1;
786*4afad4b7Schristos }
787*4afad4b7Schristos
788*4afad4b7Schristos if (l > length || l > (unsigned)PATH_MAX) {
789*4afad4b7Schristos return (ISC_R_NOSPACE);
790*4afad4b7Schristos }
791*4afad4b7Schristos
792*4afad4b7Schristos /* Check whether the full-length SHA256 hash filename exists */
793*4afad4b7Schristos err = isc_md(ISC_MD_SHA256, (const unsigned char *)base, strlen(base),
794*4afad4b7Schristos digest, &digestlen);
795*4afad4b7Schristos if (err != ISC_R_SUCCESS) {
796*4afad4b7Schristos return (err);
797*4afad4b7Schristos }
798*4afad4b7Schristos
799*4afad4b7Schristos err = digest2hex(digest, digestlen, hash, sizeof(hash));
800*4afad4b7Schristos if (err != ISC_R_SUCCESS) {
801*4afad4b7Schristos return (err);
802*4afad4b7Schristos }
803*4afad4b7Schristos
804*4afad4b7Schristos snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
805*4afad4b7Schristos dir != NULL ? "/" : "", hash, ext != NULL ? "." : "",
806*4afad4b7Schristos ext != NULL ? ext : "");
807*4afad4b7Schristos if (isc_file_exists(buf)) {
808*4afad4b7Schristos strlcpy(path, buf, length);
809*4afad4b7Schristos return (ISC_R_SUCCESS);
810*4afad4b7Schristos }
811*4afad4b7Schristos
812*4afad4b7Schristos /* Check for a truncated SHA256 hash filename */
813*4afad4b7Schristos hash[16] = '\0';
814*4afad4b7Schristos snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
815*4afad4b7Schristos dir != NULL ? "/" : "", hash, ext != NULL ? "." : "",
816*4afad4b7Schristos ext != NULL ? ext : "");
817*4afad4b7Schristos if (isc_file_exists(buf)) {
818*4afad4b7Schristos strlcpy(path, buf, length);
819*4afad4b7Schristos return (ISC_R_SUCCESS);
820*4afad4b7Schristos }
821*4afad4b7Schristos
822*4afad4b7Schristos /*
823*4afad4b7Schristos * If neither hash filename already exists, then we'll use
824*4afad4b7Schristos * the original base name if it has no disallowed characters,
825*4afad4b7Schristos * or the truncated hash name if it does.
826*4afad4b7Schristos */
827*4afad4b7Schristos if (strpbrk(base, DISALLOW) != NULL) {
828*4afad4b7Schristos strlcpy(path, buf, length);
829*4afad4b7Schristos return (ISC_R_SUCCESS);
830*4afad4b7Schristos }
831*4afad4b7Schristos
832*4afad4b7Schristos snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
833*4afad4b7Schristos dir != NULL ? "/" : "", base, ext != NULL ? "." : "",
834*4afad4b7Schristos ext != NULL ? ext : "");
835*4afad4b7Schristos strlcpy(path, buf, length);
836*4afad4b7Schristos return (ISC_R_SUCCESS);
837*4afad4b7Schristos }
838*4afad4b7Schristos
839*4afad4b7Schristos bool
isc_file_isdirwritable(const char * path)840*4afad4b7Schristos isc_file_isdirwritable(const char *path) {
841*4afad4b7Schristos return (access(path, W_OK | X_OK) == 0);
842*4afad4b7Schristos }
843