(root)/
util-linux-2.39/
lib/
fileutils.c
       1  /*
       2   * This code is in the public domain; do with it what you wish.
       3   *
       4   * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi>
       5   * Copyright (C) 2012-2020 Karel Zak <kzak@redhat.com>
       6   */
       7  #include <stdio.h>
       8  #include <stdlib.h>
       9  #include <sys/types.h>
      10  #include <sys/stat.h>
      11  #include <unistd.h>
      12  #include <sys/time.h>
      13  #include <sys/resource.h>
      14  #include <string.h>
      15  
      16  #include "c.h"
      17  #include "all-io.h"
      18  #include "fileutils.h"
      19  #include "pathnames.h"
      20  
      21  int mkstemp_cloexec(char *template)
      22  {
      23  #ifdef HAVE_MKOSTEMP
      24  	return mkostemp(template, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
      25  #else
      26  	int fd, old_flags, errno_save;
      27  
      28  	fd = mkstemp(template);
      29  	if (fd < 0)
      30  		return fd;
      31  
      32  	old_flags = fcntl(fd, F_GETFD, 0);
      33  	if (old_flags < 0)
      34  		goto unwind;
      35  	if (fcntl(fd, F_SETFD, old_flags | O_CLOEXEC) < 0)
      36  		goto unwind;
      37  
      38  	return fd;
      39  
      40  unwind:
      41  	errno_save = errno;
      42  	unlink(template);
      43  	close(fd);
      44  	errno = errno_save;
      45  
      46  	return -1;
      47  #endif
      48  }
      49  
      50  /* Create open temporary file in safe way.  Please notice that the
      51   * file permissions are -rw------- by default. */
      52  int xmkstemp(char **tmpname, const char *dir, const char *prefix)
      53  {
      54  	char *localtmp;
      55  	const char *tmpenv;
      56  	mode_t old_mode;
      57  	int fd, rc;
      58  
      59  	/* Some use cases must be capable of being moved atomically
      60  	 * with rename(2), which is the reason why dir is here.  */
      61  	tmpenv = dir ? dir : getenv("TMPDIR");
      62  	if (!tmpenv)
      63  		tmpenv = _PATH_TMP;
      64  
      65  	rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv, prefix);
      66  	if (rc < 0)
      67  		return -1;
      68  
      69  	old_mode = umask(077);
      70  	fd = mkstemp_cloexec(localtmp);
      71  	umask(old_mode);
      72  	if (fd == -1) {
      73  		free(localtmp);
      74  		localtmp = NULL;
      75  	}
      76  	*tmpname = localtmp;
      77  	return fd;
      78  }
      79  
      80  #ifdef F_DUPFD_CLOEXEC
      81  int dup_fd_cloexec(int oldfd, int lowfd)
      82  #else
      83  int dup_fd_cloexec(int oldfd, int lowfd  __attribute__((__unused__)))
      84  #endif
      85  {
      86  	int fd, flags, errno_save;
      87  
      88  #ifdef F_DUPFD_CLOEXEC
      89  	fd = fcntl(oldfd, F_DUPFD_CLOEXEC, lowfd);
      90  	if (fd >= 0)
      91  		return fd;
      92  #endif
      93  
      94  	fd = dup(oldfd);
      95  	if (fd < 0)
      96  		return fd;
      97  
      98  	flags = fcntl(fd, F_GETFD);
      99  	if (flags < 0)
     100  		goto unwind;
     101  	if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
     102  		goto unwind;
     103  
     104  	return fd;
     105  
     106  unwind:
     107  	errno_save = errno;
     108  	close(fd);
     109  	errno = errno_save;
     110  
     111  	return -1;
     112  }
     113  
     114  /*
     115   * portable getdtablesize()
     116   */
     117  unsigned int get_fd_tabsize(void)
     118  {
     119  	int m;
     120  
     121  #if defined(HAVE_GETDTABLESIZE)
     122  	m = getdtablesize();
     123  #elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
     124  	struct rlimit rl;
     125  
     126  	getrlimit(RLIMIT_NOFILE, &rl);
     127  	m = rl.rlim_cur;
     128  #elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
     129  	m = sysconf(_SC_OPEN_MAX);
     130  #else
     131  	m = OPEN_MAX;
     132  #endif
     133  	return m;
     134  }
     135  
     136  void ul_close_all_fds(unsigned int first, unsigned int last)
     137  {
     138  	struct dirent *d;
     139  	DIR *dir;
     140  
     141  	dir = opendir(_PATH_PROC_FDDIR);
     142  	if (dir) {
     143  		while ((d = xreaddir(dir))) {
     144  			char *end;
     145  			unsigned int fd;
     146  			int dfd;
     147  
     148  			errno = 0;
     149  			fd = strtoul(d->d_name, &end, 10);
     150  
     151  			if (errno || end == d->d_name || !end || *end)
     152  				continue;
     153  			dfd = dirfd(dir);
     154  			if (dfd < 0)
     155  				continue;
     156  			if ((unsigned int)dfd == fd)
     157  				continue;
     158  			if (fd < first || last < fd)
     159  				continue;
     160  			close(fd);
     161  		}
     162  		closedir(dir);
     163  	} else {
     164  		unsigned fd, tbsz = get_fd_tabsize();
     165  
     166  		for (fd = 0; fd < tbsz; fd++) {
     167  			if (first <= fd && fd <= last)
     168  				close(fd);
     169  		}
     170  	}
     171  }
     172  
     173  #ifdef TEST_PROGRAM_FILEUTILS
     174  int main(int argc, char *argv[])
     175  {
     176  	if (argc < 2)
     177  		errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds,copy-file}", argv[0]);
     178  
     179  	if (strcmp(argv[1], "--mkstemp") == 0) {
     180  		FILE *f;
     181  		char *tmpname = NULL;
     182  
     183  		f = xfmkstemp(&tmpname, NULL, "test");
     184  		unlink(tmpname);
     185  		free(tmpname);
     186  		fclose(f);
     187  
     188  	} else if (strcmp(argv[1], "--close-fds") == 0) {
     189  		ignore_result( dup(STDIN_FILENO) );
     190  		ignore_result( dup(STDIN_FILENO) );
     191  		ignore_result( dup(STDIN_FILENO) );
     192  
     193  # ifdef HAVE_CLOSE_RANGE
     194  		if (close_range(STDERR_FILENO + 1, ~0U, 0) < 0)
     195  # endif
     196  			ul_close_all_fds(STDERR_FILENO + 1, ~0U);
     197  
     198  	} else if (strcmp(argv[1], "--copy-file") == 0) {
     199  		int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO);
     200  		if (ret == UL_COPY_READ_ERROR)
     201  			err(EXIT_FAILURE, "read");
     202  		else if (ret == UL_COPY_WRITE_ERROR)
     203  			err(EXIT_FAILURE, "write");
     204  	}
     205  	return EXIT_SUCCESS;
     206  }
     207  #endif
     208  
     209  
     210  int ul_mkdir_p(const char *path, mode_t mode)
     211  {
     212  	char *p, *dir;
     213  	int rc = 0;
     214  
     215  	if (!path || !*path)
     216  		return -EINVAL;
     217  
     218  	dir = p = strdup(path);
     219  	if (!dir)
     220  		return -ENOMEM;
     221  
     222  	if (*p == '/')
     223  		p++;
     224  
     225  	while (p && *p) {
     226  		char *e = strchr(p, '/');
     227  		if (e)
     228  			*e = '\0';
     229  		if (*p) {
     230  			rc = mkdir(dir, mode);
     231  			if (rc && errno != EEXIST)
     232  				break;
     233  			rc = 0;
     234  		}
     235  		if (!e)
     236  			break;
     237  		*e = '/';
     238  		p = e + 1;
     239  	}
     240  
     241  	free(dir);
     242  	return rc;
     243  }
     244  
     245  /* returns basename and keeps dirname in the @path, if @path is "/" (root)
     246   * then returns empty string */
     247  char *stripoff_last_component(char *path)
     248  {
     249  	char *p = path ? strrchr(path, '/') : NULL;
     250  
     251  	if (!p)
     252  		return NULL;
     253  	*p = '\0';
     254  	return p + 1;
     255  }
     256  
     257  static int copy_file_simple(int from, int to)
     258  {
     259  	ssize_t nr;
     260  	char buf[BUFSIZ];
     261  
     262  	while ((nr = read_all(from, buf, sizeof(buf))) > 0)
     263  		if (write_all(to, buf, nr) == -1)
     264  			return UL_COPY_WRITE_ERROR;
     265  	if (nr < 0)
     266  		return UL_COPY_READ_ERROR;
     267  #ifdef HAVE_EXPLICIT_BZERO
     268  	explicit_bzero(buf, sizeof(buf));
     269  #endif
     270  	return 0;
     271  }
     272  
     273  /* Copies the contents of a file. Returns -1 on read error, -2 on write error. */
     274  int ul_copy_file(int from, int to)
     275  {
     276  #ifdef HAVE_SENDFILE
     277  	struct stat st;
     278  	ssize_t nw;
     279  
     280  	if (fstat(from, &st) == -1)
     281  		return UL_COPY_READ_ERROR;
     282  	if (!S_ISREG(st.st_mode))
     283  		return copy_file_simple(from, to);
     284  	if (sendfile_all(to, from, NULL, st.st_size) < 0)
     285  		return copy_file_simple(from, to);
     286  	/* ensure we either get an EOF or an error */
     287  	while ((nw = sendfile_all(to, from, NULL, 16*1024*1024)) != 0)
     288  		if (nw < 0)
     289  			return copy_file_simple(from, to);
     290  	return 0;
     291  #else
     292  	return copy_file_simple(from, to);
     293  #endif
     294  }
     295  
     296  int ul_reopen(int fd, int flags)
     297  {
     298  	ssize_t ssz;
     299  	char buf[PATH_MAX];
     300  	char fdpath[ sizeof(_PATH_PROC_FDDIR) + sizeof(stringify_value(INT_MAX)) ];
     301  
     302  	snprintf(fdpath, sizeof(fdpath), _PATH_PROC_FDDIR "/%d", fd);
     303  
     304  	ssz = readlink(fdpath, buf, sizeof(buf) - 1);
     305  	if (ssz < 0)
     306  		return -errno;
     307  
     308  	assert(ssz > 0);
     309  
     310  	buf[ssz] = '\0';
     311  
     312  	return open(buf, flags);
     313  }