18a1362adSmaya/*
28a1362adSmaya * Copyright 2019 Intel Corporation
38a1362adSmaya * SPDX-License-Identifier: MIT
48a1362adSmaya */
58a1362adSmaya
68a1362adSmaya#include "os_file.h"
77ec681f3Smrg#include "detect_os.h"
88a1362adSmaya
98a1362adSmaya#include <errno.h>
107ec681f3Smrg#include <fcntl.h>
118a1362adSmaya#include <stdlib.h>
127ec681f3Smrg#include <sys/stat.h>
137ec681f3Smrg
147ec681f3Smrg#if DETECT_OS_WINDOWS
157ec681f3Smrg#include <io.h>
167ec681f3Smrg#define open _open
177ec681f3Smrg#define fdopen _fdopen
187ec681f3Smrg#define O_CREAT _O_CREAT
197ec681f3Smrg#define O_EXCL _O_EXCL
207ec681f3Smrg#define O_WRONLY _O_WRONLY
217ec681f3Smrg#else
227ec681f3Smrg#include <unistd.h>
237ec681f3Smrg#ifndef F_DUPFD_CLOEXEC
247ec681f3Smrg#define F_DUPFD_CLOEXEC 1030
257ec681f3Smrg#endif
267ec681f3Smrg#endif
277ec681f3Smrg
287ec681f3Smrg
297ec681f3SmrgFILE *
307ec681f3Smrgos_file_create_unique(const char *filename, int filemode)
317ec681f3Smrg{
327ec681f3Smrg   int fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, filemode);
337ec681f3Smrg   if (fd == -1)
347ec681f3Smrg      return NULL;
357ec681f3Smrg   return fdopen(fd, "w");
367ec681f3Smrg}
377ec681f3Smrg
387ec681f3Smrg
397ec681f3Smrg#if DETECT_OS_WINDOWS
407ec681f3Smrgint
417ec681f3Smrgos_dupfd_cloexec(int fd)
427ec681f3Smrg{
437ec681f3Smrg   /*
447ec681f3Smrg    * On Windows child processes don't inherit handles by default:
457ec681f3Smrg    * https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873
467ec681f3Smrg    */
477ec681f3Smrg   return dup(fd);
487ec681f3Smrg}
497ec681f3Smrg#else
507ec681f3Smrgint
517ec681f3Smrgos_dupfd_cloexec(int fd)
527ec681f3Smrg{
537ec681f3Smrg   int minfd = 3;
547ec681f3Smrg   int newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
557ec681f3Smrg
567ec681f3Smrg   if (newfd >= 0)
577ec681f3Smrg      return newfd;
587ec681f3Smrg
597ec681f3Smrg   if (errno != EINVAL)
607ec681f3Smrg      return -1;
617ec681f3Smrg
627ec681f3Smrg   newfd = fcntl(fd, F_DUPFD, minfd);
637ec681f3Smrg
647ec681f3Smrg   if (newfd < 0)
657ec681f3Smrg      return -1;
668a1362adSmaya
677ec681f3Smrg   long flags = fcntl(newfd, F_GETFD);
687ec681f3Smrg   if (flags == -1) {
697ec681f3Smrg      close(newfd);
707ec681f3Smrg      return -1;
717ec681f3Smrg   }
727ec681f3Smrg
737ec681f3Smrg   if (fcntl(newfd, F_SETFD, flags | FD_CLOEXEC) == -1) {
747ec681f3Smrg      close(newfd);
757ec681f3Smrg      return -1;
767ec681f3Smrg   }
777ec681f3Smrg
787ec681f3Smrg   return newfd;
797ec681f3Smrg}
807ec681f3Smrg#endif
818a1362adSmaya
828a1362adSmaya#include <fcntl.h>
838a1362adSmaya#include <sys/stat.h>
848a1362adSmaya
857ec681f3Smrg#if DETECT_OS_WINDOWS
867ec681f3Smrgtypedef ptrdiff_t ssize_t;
877ec681f3Smrg#endif
888a1362adSmaya
898a1362adSmayastatic ssize_t
908a1362adSmayareadN(int fd, char *buf, size_t len)
918a1362adSmaya{
927ec681f3Smrg   /* err was initially set to -ENODATA but in some BSD systems
937ec681f3Smrg    * ENODATA is not defined and ENOATTR is used instead.
947ec681f3Smrg    * As err is not returned by any function it can be initialized
957ec681f3Smrg    * to -EFAULT that exists everywhere.
967ec681f3Smrg    */
977ec681f3Smrg   int err = -EFAULT;
988a1362adSmaya   size_t total = 0;
998a1362adSmaya   do {
1008a1362adSmaya      ssize_t ret = read(fd, buf + total, len - total);
1018a1362adSmaya
1028a1362adSmaya      if (ret < 0)
1038a1362adSmaya         ret = -errno;
1048a1362adSmaya
1058a1362adSmaya      if (ret == -EINTR || ret == -EAGAIN)
1068a1362adSmaya         continue;
1078a1362adSmaya
1088a1362adSmaya      if (ret <= 0) {
1098a1362adSmaya         err = ret;
1108a1362adSmaya         break;
1118a1362adSmaya      }
1128a1362adSmaya
1138a1362adSmaya      total += ret;
1148a1362adSmaya   } while (total != len);
1158a1362adSmaya
1167ec681f3Smrg   return total ? (ssize_t)total : err;
1178a1362adSmaya}
1188a1362adSmaya
1197ec681f3Smrg#ifndef O_BINARY
1207ec681f3Smrg/* Unix makes no distinction between text and binary files. */
1217ec681f3Smrg#define O_BINARY 0
1227ec681f3Smrg#endif
1237ec681f3Smrg
1248a1362adSmayachar *
1257ec681f3Smrgos_read_file(const char *filename, size_t *size)
1268a1362adSmaya{
1278a1362adSmaya   /* Note that this also serves as a slight margin to avoid a 2x grow when
1288a1362adSmaya    * the file is just a few bytes larger when we read it than when we
1298a1362adSmaya    * fstat'ed it.
1308a1362adSmaya    * The string's NULL terminator is also included in here.
1318a1362adSmaya    */
1328a1362adSmaya   size_t len = 64;
1338a1362adSmaya
1347ec681f3Smrg   int fd = open(filename, O_RDONLY | O_BINARY);
1358a1362adSmaya   if (fd == -1) {
1368a1362adSmaya      /* errno set by open() */
1378a1362adSmaya      return NULL;
1388a1362adSmaya   }
1398a1362adSmaya
1408a1362adSmaya   /* Pre-allocate a buffer at least the size of the file if we can read
1418a1362adSmaya    * that information.
1428a1362adSmaya    */
1438a1362adSmaya   struct stat stat;
1448a1362adSmaya   if (fstat(fd, &stat) == 0)
1458a1362adSmaya      len += stat.st_size;
1468a1362adSmaya
1478a1362adSmaya   char *buf = malloc(len);
1488a1362adSmaya   if (!buf) {
1498a1362adSmaya      close(fd);
1508a1362adSmaya      errno = -ENOMEM;
1518a1362adSmaya      return NULL;
1528a1362adSmaya   }
1538a1362adSmaya
1547ec681f3Smrg   ssize_t actually_read;
1558a1362adSmaya   size_t offset = 0, remaining = len - 1;
1567ec681f3Smrg   while ((actually_read = readN(fd, buf + offset, remaining)) == (ssize_t)remaining) {
1578a1362adSmaya      char *newbuf = realloc(buf, 2 * len);
1588a1362adSmaya      if (!newbuf) {
1598a1362adSmaya         free(buf);
1608a1362adSmaya         close(fd);
1618a1362adSmaya         errno = -ENOMEM;
1628a1362adSmaya         return NULL;
1638a1362adSmaya      }
1648a1362adSmaya
1658a1362adSmaya      buf = newbuf;
1668a1362adSmaya      len *= 2;
1677ec681f3Smrg      offset += actually_read;
1688a1362adSmaya      remaining = len - offset - 1;
1698a1362adSmaya   }
1708a1362adSmaya
1718a1362adSmaya   close(fd);
1728a1362adSmaya
1737ec681f3Smrg   if (actually_read > 0)
1747ec681f3Smrg      offset += actually_read;
1758a1362adSmaya
1768a1362adSmaya   /* Final resize to actual size */
1778a1362adSmaya   len = offset + 1;
1788a1362adSmaya   char *newbuf = realloc(buf, len);
1798a1362adSmaya   if (!newbuf) {
1808a1362adSmaya      free(buf);
1818a1362adSmaya      errno = -ENOMEM;
1828a1362adSmaya      return NULL;
1838a1362adSmaya   }
1848a1362adSmaya   buf = newbuf;
1858a1362adSmaya
1868a1362adSmaya   buf[offset] = '\0';
1878a1362adSmaya
1887ec681f3Smrg   if (size)
1897ec681f3Smrg      *size = offset;
1907ec681f3Smrg
1918a1362adSmaya   return buf;
1928a1362adSmaya}
1938a1362adSmaya
1947ec681f3Smrg#if DETECT_OS_LINUX
1957ec681f3Smrg
1967ec681f3Smrg#include <sys/syscall.h>
1977ec681f3Smrg#include <unistd.h>
1987ec681f3Smrg
1997ec681f3Smrg/* copied from <linux/kcmp.h> */
2007ec681f3Smrg#define KCMP_FILE 0
2017ec681f3Smrg
2027ec681f3Smrgint
2037ec681f3Smrgos_same_file_description(int fd1, int fd2)
2047ec681f3Smrg{
2057ec681f3Smrg   pid_t pid = getpid();
2067ec681f3Smrg
2077ec681f3Smrg   /* Same file descriptor trivially implies same file description */
2087ec681f3Smrg   if (fd1 == fd2)
2097ec681f3Smrg      return 0;
2107ec681f3Smrg
2117ec681f3Smrg   return syscall(SYS_kcmp, pid, pid, KCMP_FILE, fd1, fd2);
2127ec681f3Smrg}
2137ec681f3Smrg
2148a1362adSmaya#else
2158a1362adSmaya
2167ec681f3Smrgint
2177ec681f3Smrgos_same_file_description(int fd1, int fd2)
2188a1362adSmaya{
2197ec681f3Smrg   /* Same file descriptor trivially implies same file description */
2207ec681f3Smrg   if (fd1 == fd2)
2217ec681f3Smrg      return 0;
2227ec681f3Smrg
2237ec681f3Smrg   /* Otherwise we can't tell */
2247ec681f3Smrg   return -1;
2258a1362adSmaya}
2268a1362adSmaya
2278a1362adSmaya#endif
228