Home | History | Annotate | Line # | Download | only in util
      1 /*
      2  * Copyright 2019 Intel Corporation
      3  * SPDX-License-Identifier: MIT
      4  */
      5 
      6 #include "os_file.h"
      7 #include "detect_os.h"
      8 
      9 #include <errno.h>
     10 #include <fcntl.h>
     11 #include <stdlib.h>
     12 #include <sys/stat.h>
     13 
     14 #if DETECT_OS_WINDOWS
     15 #include <io.h>
     16 #define open _open
     17 #define fdopen _fdopen
     18 #define O_CREAT _O_CREAT
     19 #define O_EXCL _O_EXCL
     20 #define O_WRONLY _O_WRONLY
     21 #else
     22 #include <unistd.h>
     23 #ifndef F_DUPFD_CLOEXEC
     24 #define F_DUPFD_CLOEXEC 1030
     25 #endif
     26 #endif
     27 
     28 
     29 FILE *
     30 os_file_create_unique(const char *filename, int filemode)
     31 {
     32    int fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, filemode);
     33    if (fd == -1)
     34       return NULL;
     35    return fdopen(fd, "w");
     36 }
     37 
     38 
     39 #if DETECT_OS_WINDOWS
     40 int
     41 os_dupfd_cloexec(int fd)
     42 {
     43    /*
     44     * On Windows child processes don't inherit handles by default:
     45     * https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873
     46     */
     47    return dup(fd);
     48 }
     49 #else
     50 int
     51 os_dupfd_cloexec(int fd)
     52 {
     53    int minfd = 3;
     54    int newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
     55 
     56    if (newfd >= 0)
     57       return newfd;
     58 
     59    if (errno != EINVAL)
     60       return -1;
     61 
     62    newfd = fcntl(fd, F_DUPFD, minfd);
     63 
     64    if (newfd < 0)
     65       return -1;
     66 
     67    long flags = fcntl(newfd, F_GETFD);
     68    if (flags == -1) {
     69       close(newfd);
     70       return -1;
     71    }
     72 
     73    if (fcntl(newfd, F_SETFD, flags | FD_CLOEXEC) == -1) {
     74       close(newfd);
     75       return -1;
     76    }
     77 
     78    return newfd;
     79 }
     80 #endif
     81 
     82 #include <fcntl.h>
     83 #include <sys/stat.h>
     84 
     85 #if DETECT_OS_WINDOWS
     86 typedef ptrdiff_t ssize_t;
     87 #endif
     88 
     89 static ssize_t
     90 readN(int fd, char *buf, size_t len)
     91 {
     92    /* err was initially set to -ENODATA but in some BSD systems
     93     * ENODATA is not defined and ENOATTR is used instead.
     94     * As err is not returned by any function it can be initialized
     95     * to -EFAULT that exists everywhere.
     96     */
     97    int err = -EFAULT;
     98    size_t total = 0;
     99    do {
    100       ssize_t ret = read(fd, buf + total, len - total);
    101 
    102       if (ret < 0)
    103          ret = -errno;
    104 
    105       if (ret == -EINTR || ret == -EAGAIN)
    106          continue;
    107 
    108       if (ret <= 0) {
    109          err = ret;
    110          break;
    111       }
    112 
    113       total += ret;
    114    } while (total != len);
    115 
    116    return total ? (ssize_t)total : err;
    117 }
    118 
    119 #ifndef O_BINARY
    120 /* Unix makes no distinction between text and binary files. */
    121 #define O_BINARY 0
    122 #endif
    123 
    124 char *
    125 os_read_file(const char *filename, size_t *size)
    126 {
    127    /* Note that this also serves as a slight margin to avoid a 2x grow when
    128     * the file is just a few bytes larger when we read it than when we
    129     * fstat'ed it.
    130     * The string's NULL terminator is also included in here.
    131     */
    132    size_t len = 64;
    133 
    134    int fd = open(filename, O_RDONLY | O_BINARY);
    135    if (fd == -1) {
    136       /* errno set by open() */
    137       return NULL;
    138    }
    139 
    140    /* Pre-allocate a buffer at least the size of the file if we can read
    141     * that information.
    142     */
    143    struct stat stat;
    144    if (fstat(fd, &stat) == 0)
    145       len += stat.st_size;
    146 
    147    char *buf = malloc(len);
    148    if (!buf) {
    149       close(fd);
    150       errno = -ENOMEM;
    151       return NULL;
    152    }
    153 
    154    ssize_t actually_read;
    155    size_t offset = 0, remaining = len - 1;
    156    while ((actually_read = readN(fd, buf + offset, remaining)) == (ssize_t)remaining) {
    157       char *newbuf = realloc(buf, 2 * len);
    158       if (!newbuf) {
    159          free(buf);
    160          close(fd);
    161          errno = -ENOMEM;
    162          return NULL;
    163       }
    164 
    165       buf = newbuf;
    166       len *= 2;
    167       offset += actually_read;
    168       remaining = len - offset - 1;
    169    }
    170 
    171    close(fd);
    172 
    173    if (actually_read > 0)
    174       offset += actually_read;
    175 
    176    /* Final resize to actual size */
    177    len = offset + 1;
    178    char *newbuf = realloc(buf, len);
    179    if (!newbuf) {
    180       free(buf);
    181       errno = -ENOMEM;
    182       return NULL;
    183    }
    184    buf = newbuf;
    185 
    186    buf[offset] = '\0';
    187 
    188    if (size)
    189       *size = offset;
    190 
    191    return buf;
    192 }
    193 
    194 #if DETECT_OS_LINUX
    195 
    196 #include <sys/syscall.h>
    197 #include <unistd.h>
    198 
    199 /* copied from <linux/kcmp.h> */
    200 #define KCMP_FILE 0
    201 
    202 int
    203 os_same_file_description(int fd1, int fd2)
    204 {
    205    pid_t pid = getpid();
    206 
    207    /* Same file descriptor trivially implies same file description */
    208    if (fd1 == fd2)
    209       return 0;
    210 
    211    return syscall(SYS_kcmp, pid, pid, KCMP_FILE, fd1, fd2);
    212 }
    213 
    214 #else
    215 
    216 int
    217 os_same_file_description(int fd1, int fd2)
    218 {
    219    /* Same file descriptor trivially implies same file description */
    220    if (fd1 == fd2)
    221       return 0;
    222 
    223    /* Otherwise we can't tell */
    224    return -1;
    225 }
    226 
    227 #endif
    228