Home | History | Annotate | Line # | Download | only in unix
      1  1.1  christos /*	$NetBSD: dir.c,v 1.1 2024/02/18 20:57:56 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*
      4  1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  1.1  christos  *
      6  1.1  christos  * SPDX-License-Identifier: MPL-2.0
      7  1.1  christos  *
      8  1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9  1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  1.1  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  1.1  christos  *
     12  1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13  1.1  christos  * information regarding copyright ownership.
     14  1.1  christos  */
     15  1.1  christos 
     16  1.1  christos /*! \file */
     17  1.1  christos 
     18  1.1  christos #include <ctype.h>
     19  1.1  christos #include <errno.h>
     20  1.1  christos #include <sys/stat.h>
     21  1.1  christos #include <sys/types.h>
     22  1.1  christos #include <unistd.h>
     23  1.1  christos 
     24  1.1  christos #include <isc/dir.h>
     25  1.1  christos #include <isc/magic.h>
     26  1.1  christos #include <isc/netdb.h>
     27  1.1  christos #include <isc/print.h>
     28  1.1  christos #include <isc/string.h>
     29  1.1  christos #include <isc/util.h>
     30  1.1  christos 
     31  1.1  christos #include "errno2result.h"
     32  1.1  christos 
     33  1.1  christos #define ISC_DIR_MAGIC  ISC_MAGIC('D', 'I', 'R', '*')
     34  1.1  christos #define VALID_DIR(dir) ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC)
     35  1.1  christos 
     36  1.1  christos void
     37  1.1  christos isc_dir_init(isc_dir_t *dir) {
     38  1.1  christos 	REQUIRE(dir != NULL);
     39  1.1  christos 
     40  1.1  christos 	dir->entry.name[0] = '\0';
     41  1.1  christos 	dir->entry.length = 0;
     42  1.1  christos 
     43  1.1  christos 	dir->handle = NULL;
     44  1.1  christos 
     45  1.1  christos 	dir->magic = ISC_DIR_MAGIC;
     46  1.1  christos }
     47  1.1  christos 
     48  1.1  christos /*!
     49  1.1  christos  * \brief Allocate workspace and open directory stream. If either one fails,
     50  1.1  christos  * NULL will be returned.
     51  1.1  christos  */
     52  1.1  christos isc_result_t
     53  1.1  christos isc_dir_open(isc_dir_t *dir, const char *dirname) {
     54  1.1  christos 	char *p;
     55  1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
     56  1.1  christos 
     57  1.1  christos 	REQUIRE(VALID_DIR(dir));
     58  1.1  christos 	REQUIRE(dirname != NULL);
     59  1.1  christos 
     60  1.1  christos 	/*
     61  1.1  christos 	 * Copy directory name.  Need to have enough space for the name,
     62  1.1  christos 	 * a possible path separator, the wildcard, and the final NUL.
     63  1.1  christos 	 */
     64  1.1  christos 	if (strlen(dirname) + 3 > sizeof(dir->dirname)) {
     65  1.1  christos 		/* XXXDCL ? */
     66  1.1  christos 		return (ISC_R_NOSPACE);
     67  1.1  christos 	}
     68  1.1  christos 	strlcpy(dir->dirname, dirname, sizeof(dir->dirname));
     69  1.1  christos 
     70  1.1  christos 	/*
     71  1.1  christos 	 * Append path separator, if needed, and "*".
     72  1.1  christos 	 */
     73  1.1  christos 	p = dir->dirname + strlen(dir->dirname);
     74  1.1  christos 	if (dir->dirname < p && *(p - 1) != '/') {
     75  1.1  christos 		*p++ = '/';
     76  1.1  christos 	}
     77  1.1  christos 	*p++ = '*';
     78  1.1  christos 	*p = '\0';
     79  1.1  christos 
     80  1.1  christos 	/*
     81  1.1  christos 	 * Open stream.
     82  1.1  christos 	 */
     83  1.1  christos 	dir->handle = opendir(dirname);
     84  1.1  christos 
     85  1.1  christos 	if (dir->handle == NULL) {
     86  1.1  christos 		return (isc__errno2result(errno));
     87  1.1  christos 	}
     88  1.1  christos 
     89  1.1  christos 	return (result);
     90  1.1  christos }
     91  1.1  christos 
     92  1.1  christos /*!
     93  1.1  christos  * \brief Return previously retrieved file or get next one.
     94  1.1  christos  *
     95  1.1  christos  * Unix's dirent has
     96  1.1  christos  * separate open and read functions, but the Win32 and DOS interfaces open
     97  1.1  christos  * the dir stream and reads the first file in one operation.
     98  1.1  christos  */
     99  1.1  christos isc_result_t
    100  1.1  christos isc_dir_read(isc_dir_t *dir) {
    101  1.1  christos 	struct dirent *entry;
    102  1.1  christos 
    103  1.1  christos 	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
    104  1.1  christos 
    105  1.1  christos 	/*
    106  1.1  christos 	 * Fetch next file in directory.
    107  1.1  christos 	 */
    108  1.1  christos 	entry = readdir(dir->handle);
    109  1.1  christos 
    110  1.1  christos 	if (entry == NULL) {
    111  1.1  christos 		return (ISC_R_NOMORE);
    112  1.1  christos 	}
    113  1.1  christos 
    114  1.1  christos 	/*
    115  1.1  christos 	 * Make sure that the space for the name is long enough.
    116  1.1  christos 	 */
    117  1.1  christos 	if (sizeof(dir->entry.name) <= strlen(entry->d_name)) {
    118  1.1  christos 		return (ISC_R_UNEXPECTED);
    119  1.1  christos 	}
    120  1.1  christos 
    121  1.1  christos 	strlcpy(dir->entry.name, entry->d_name, sizeof(dir->entry.name));
    122  1.1  christos 
    123  1.1  christos 	/*
    124  1.1  christos 	 * Some dirents have d_namlen, but it is not portable.
    125  1.1  christos 	 */
    126  1.1  christos 	dir->entry.length = strlen(entry->d_name);
    127  1.1  christos 
    128  1.1  christos 	return (ISC_R_SUCCESS);
    129  1.1  christos }
    130  1.1  christos 
    131  1.1  christos /*!
    132  1.1  christos  * \brief Close directory stream.
    133  1.1  christos  */
    134  1.1  christos void
    135  1.1  christos isc_dir_close(isc_dir_t *dir) {
    136  1.1  christos 	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
    137  1.1  christos 
    138  1.1  christos 	(void)closedir(dir->handle);
    139  1.1  christos 	dir->handle = NULL;
    140  1.1  christos }
    141  1.1  christos 
    142  1.1  christos /*!
    143  1.1  christos  * \brief Reposition directory stream at start.
    144  1.1  christos  */
    145  1.1  christos isc_result_t
    146  1.1  christos isc_dir_reset(isc_dir_t *dir) {
    147  1.1  christos 	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
    148  1.1  christos 
    149  1.1  christos 	rewinddir(dir->handle);
    150  1.1  christos 
    151  1.1  christos 	return (ISC_R_SUCCESS);
    152  1.1  christos }
    153  1.1  christos 
    154  1.1  christos isc_result_t
    155  1.1  christos isc_dir_chdir(const char *dirname) {
    156  1.1  christos 	/*!
    157  1.1  christos 	 * \brief Change the current directory to 'dirname'.
    158  1.1  christos 	 */
    159  1.1  christos 
    160  1.1  christos 	REQUIRE(dirname != NULL);
    161  1.1  christos 
    162  1.1  christos 	if (chdir(dirname) < 0) {
    163  1.1  christos 		return (isc__errno2result(errno));
    164  1.1  christos 	}
    165  1.1  christos 
    166  1.1  christos 	return (ISC_R_SUCCESS);
    167  1.1  christos }
    168  1.1  christos 
    169  1.1  christos isc_result_t
    170  1.1  christos isc_dir_chroot(const char *dirname) {
    171  1.1  christos #ifdef HAVE_CHROOT
    172  1.1  christos 	void *tmp;
    173  1.1  christos #endif /* ifdef HAVE_CHROOT */
    174  1.1  christos 
    175  1.1  christos 	REQUIRE(dirname != NULL);
    176  1.1  christos 
    177  1.1  christos #ifdef HAVE_CHROOT
    178  1.1  christos 	/*
    179  1.1  christos 	 * Try to use getservbyname and getprotobyname before chroot.
    180  1.1  christos 	 * If WKS records are used in a zone under chroot, Name Service Switch
    181  1.1  christos 	 * may fail to load library in chroot.
    182  1.1  christos 	 * Do not report errors if it fails, we do not need any result now.
    183  1.1  christos 	 */
    184  1.1  christos 	tmp = getprotobyname("udp");
    185  1.1  christos 	if (tmp != NULL) {
    186  1.1  christos 		(void)getservbyname("domain", "udp");
    187  1.1  christos 	}
    188  1.1  christos 
    189  1.1  christos 	if (chroot(dirname) < 0 || chdir("/") < 0) {
    190  1.1  christos 		return (isc__errno2result(errno));
    191  1.1  christos 	}
    192  1.1  christos 
    193  1.1  christos 	return (ISC_R_SUCCESS);
    194  1.1  christos #else  /* ifdef HAVE_CHROOT */
    195  1.1  christos 	return (ISC_R_NOTIMPLEMENTED);
    196  1.1  christos #endif /* ifdef HAVE_CHROOT */
    197  1.1  christos }
    198  1.1  christos 
    199  1.1  christos isc_result_t
    200  1.1  christos isc_dir_createunique(char *templet) {
    201  1.1  christos 	isc_result_t result;
    202  1.1  christos 	char *x;
    203  1.1  christos 	char *p;
    204  1.1  christos 	int i;
    205  1.1  christos 	int pid;
    206  1.1  christos 
    207  1.1  christos 	REQUIRE(templet != NULL);
    208  1.1  christos 
    209  1.1  christos 	/*!
    210  1.1  christos 	 * \brief mkdtemp is not portable, so this emulates it.
    211  1.1  christos 	 */
    212  1.1  christos 
    213  1.1  christos 	pid = getpid();
    214  1.1  christos 
    215  1.1  christos 	/*
    216  1.1  christos 	 * Replace trailing Xs with the process-id, zero-filled.
    217  1.1  christos 	 */
    218  1.1  christos 	for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet;
    219  1.1  christos 	     x--, pid /= 10)
    220  1.1  christos 	{
    221  1.1  christos 		*x = pid % 10 + '0';
    222  1.1  christos 	}
    223  1.1  christos 
    224  1.1  christos 	x++; /* Set x to start of ex-Xs. */
    225  1.1  christos 
    226  1.1  christos 	do {
    227  1.1  christos 		i = mkdir(templet, 0700);
    228  1.1  christos 		if (i == 0 || errno != EEXIST) {
    229  1.1  christos 			break;
    230  1.1  christos 		}
    231  1.1  christos 
    232  1.1  christos 		/*
    233  1.1  christos 		 * The BSD algorithm.
    234  1.1  christos 		 */
    235  1.1  christos 		p = x;
    236  1.1  christos 		while (*p != '\0') {
    237  1.1  christos 			if (isdigit((unsigned char)*p)) {
    238  1.1  christos 				*p = 'a';
    239  1.1  christos 			} else if (*p != 'z') {
    240  1.1  christos 				++*p;
    241  1.1  christos 			} else {
    242  1.1  christos 				/*
    243  1.1  christos 				 * Reset character and move to next.
    244  1.1  christos 				 */
    245  1.1  christos 				*p++ = 'a';
    246  1.1  christos 				continue;
    247  1.1  christos 			}
    248  1.1  christos 
    249  1.1  christos 			break;
    250  1.1  christos 		}
    251  1.1  christos 
    252  1.1  christos 		if (*p == '\0') {
    253  1.1  christos 			/*
    254  1.1  christos 			 * Tried all combinations.  errno should already
    255  1.1  christos 			 * be EEXIST, but ensure it is anyway for
    256  1.1  christos 			 * isc__errno2result().
    257  1.1  christos 			 */
    258  1.1  christos 			errno = EEXIST;
    259  1.1  christos 			break;
    260  1.1  christos 		}
    261  1.1  christos 	} while (1);
    262  1.1  christos 
    263  1.1  christos 	if (i == -1) {
    264  1.1  christos 		result = isc__errno2result(errno);
    265  1.1  christos 	} else {
    266  1.1  christos 		result = ISC_R_SUCCESS;
    267  1.1  christos 	}
    268  1.1  christos 
    269  1.1  christos 	return (result);
    270  1.1  christos }
    271