Coverage Report

Created: 2026-04-02 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/bio/file.cc
Line
Count
Source
1
// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#if defined(__linux) || defined(__sun) || defined(__hpux)
16
// Following definition aliases fopen to fopen64 on above mentioned
17
// platforms. This makes it possible to open and sequentially access
18
// files larger than 2GB from 32-bit application. It does not allow to
19
// traverse them beyond 2GB with fseek/ftell, but on the other hand *no*
20
// 32-bit platform permits that, not with fseek/ftell. Not to mention
21
// that breaking 2GB limit for seeking would require surgery to *our*
22
// API. But sequential access suffices for practical cases when you
23
// can run into large files, such as fingerprinting, so we can let API
24
// alone. For reference, the list of 32-bit platforms which allow for
25
// sequential access of large files without extra "magic" comprise *BSD,
26
// Darwin, IRIX...
27
#ifndef _FILE_OFFSET_BITS
28
#define _FILE_OFFSET_BITS 64
29
#endif
30
#endif
31
32
#include <openssl/bio.h>
33
34
#include <assert.h>
35
#include <errno.h>
36
#include <stdio.h>
37
#include <string.h>
38
39
#include <openssl/err.h>
40
#include <openssl/mem.h>
41
42
#include "../internal.h"
43
#include "internal.h"
44
45
#if defined(OPENSSL_WINDOWS)
46
#include <fcntl.h>
47
#include <io.h>
48
#endif
49
50
0
#define BIO_FP_READ 0x02
51
0
#define BIO_FP_WRITE 0x04
52
0
#define BIO_FP_APPEND 0x08
53
54
#if !defined(OPENSSL_NO_FILESYSTEM)
55
0
#define fopen_if_available fopen
56
#else
57
static FILE *fopen_if_available(const char *path, const char *mode) {
58
  errno = ENOENT;
59
  return nullptr;
60
}
61
#endif
62
63
0
BIO *BIO_new_file(const char *filename, const char *mode) {
64
0
  BIO *ret;
65
0
  FILE *file;
66
67
0
  file = fopen_if_available(filename, mode);
68
0
  if (file == nullptr) {
69
0
    OPENSSL_PUT_SYSTEM_ERROR();
70
71
0
    ERR_add_error_data(5, "fopen('", filename, "','", mode, "')");
72
0
    if (errno == ENOENT) {
73
0
      OPENSSL_PUT_ERROR(BIO, BIO_R_NO_SUCH_FILE);
74
0
    } else {
75
0
      OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
76
0
    }
77
0
    return nullptr;
78
0
  }
79
80
0
  ret = BIO_new_fp(file, BIO_CLOSE);
81
0
  if (ret == nullptr) {
82
0
    fclose(file);
83
0
    return nullptr;
84
0
  }
85
86
0
  return ret;
87
0
}
88
89
0
BIO *BIO_new_fp(FILE *stream, int flags) {
90
0
  BIO *ret = BIO_new(BIO_s_file());
91
0
  if (ret == nullptr) {
92
0
    return nullptr;
93
0
  }
94
95
0
  BIO_set_fp(ret, stream, flags);
96
0
  return ret;
97
0
}
98
99
0
static int file_free(BIO *bio) {
100
0
  if (!BIO_get_shutdown(bio)) {
101
0
    return 1;
102
0
  }
103
104
0
  if (BIO_get_init(bio) && BIO_get_data(bio) != nullptr) {
105
0
    fclose(reinterpret_cast<FILE *>(BIO_get_data(bio)));
106
0
    BIO_set_data(bio, nullptr);
107
0
  }
108
0
  BIO_set_init(bio, 0);
109
110
0
  return 1;
111
0
}
112
113
0
static int file_read(BIO *b, char *out, int outl) {
114
0
  if (!BIO_get_init(b)) {
115
0
    return 0;
116
0
  }
117
118
0
  size_t ret = fread(out, 1, outl, (FILE *)BIO_get_data(b));
119
0
  if (ret == 0 && ferror((FILE *)BIO_get_data(b))) {
120
0
    OPENSSL_PUT_SYSTEM_ERROR();
121
0
    OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
122
0
    return -1;
123
0
  }
124
125
  // fread reads at most |outl| bytes, so |ret| fits in an int.
126
0
  return (int)ret;
127
0
}
128
129
0
static int file_write(BIO *b, const char *in, int inl) {
130
0
  if (!BIO_get_init(b)) {
131
0
    return 0;
132
0
  }
133
134
0
  int ret = (int)fwrite(in, inl, 1, (FILE *)BIO_get_data(b));
135
0
  if (ret > 0) {
136
0
    ret = inl;
137
0
  }
138
0
  return ret;
139
0
}
140
141
0
static long file_ctrl(BIO *b, int cmd, long num, void *ptr) {
142
0
  FILE *fp = static_cast<FILE *>(BIO_get_data(b));
143
0
  switch (cmd) {
144
0
    case BIO_CTRL_RESET:
145
0
      num = 0;
146
0
      [[fallthrough]];
147
0
    case BIO_C_FILE_SEEK:
148
0
      return fseek(fp, num, 0);
149
0
    case BIO_CTRL_EOF:
150
      // feof may return any non-zero value for EOF, but we must return 1.
151
0
      return feof(fp) != 0;
152
0
    case BIO_C_FILE_TELL:
153
0
    case BIO_CTRL_INFO:
154
0
      return ftell(fp);
155
0
    case BIO_C_SET_FILE_PTR:
156
0
      file_free(b);
157
0
      static_assert((BIO_CLOSE & BIO_FP_TEXT) == 0,
158
0
                    "BIO_CLOSE and BIO_FP_TEXT must not collide");
159
#if defined(OPENSSL_WINDOWS)
160
      // If |BIO_FP_TEXT| is not set, OpenSSL will switch the file to binary
161
      // mode. BoringSSL intentionally diverges here because it means code
162
      // tested under POSIX will inadvertently change the state of |FILE|
163
      // objects when wrapping them in a |BIO|.
164
      if (num & BIO_FP_TEXT) {
165
        _setmode(_fileno(reinterpret_cast<FILE *>(ptr)), _O_TEXT);
166
      }
167
#endif
168
0
      BIO_set_shutdown(b, static_cast<int>(num) & BIO_CLOSE);
169
0
      BIO_set_data(b, ptr);
170
0
      BIO_set_init(b, 1);
171
0
      return 1;
172
0
    case BIO_C_SET_FILENAME:
173
0
      file_free(b);
174
0
      BIO_set_shutdown(b, static_cast<int>(num) & BIO_CLOSE);
175
0
      const char *mode;
176
0
      if (num & BIO_FP_APPEND) {
177
0
        if (num & BIO_FP_READ) {
178
0
          mode = "ab+";
179
0
        } else {
180
0
          mode = "ab";
181
0
        }
182
0
      } else if ((num & BIO_FP_READ) && (num & BIO_FP_WRITE)) {
183
0
        mode = "rb+";
184
0
      } else if (num & BIO_FP_WRITE) {
185
0
        mode = "wb";
186
0
      } else if (num & BIO_FP_READ) {
187
0
        mode = "rb";
188
0
      } else {
189
0
        OPENSSL_PUT_ERROR(BIO, BIO_R_BAD_FOPEN_MODE);
190
0
        return 0;
191
0
      }
192
0
      fp = fopen_if_available(reinterpret_cast<const char *>(ptr), mode);
193
0
      if (fp == nullptr) {
194
0
        OPENSSL_PUT_SYSTEM_ERROR();
195
0
        ERR_add_error_data(5, "fopen('", ptr, "','", mode, "')");
196
0
        OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
197
0
        return 0;
198
0
      }
199
0
      BIO_set_data(b, fp);
200
0
      BIO_set_init(b, 1);
201
0
      return 1;
202
0
    case BIO_C_GET_FILE_PTR:
203
      // the ptr parameter is actually a FILE ** in this case.
204
0
      if (ptr != nullptr) {
205
0
        FILE **out = static_cast<FILE **>(ptr);
206
0
        *out = fp;
207
0
      }
208
0
      return 1;
209
0
    case BIO_CTRL_GET_CLOSE:
210
0
      return BIO_get_shutdown(b);
211
0
    case BIO_CTRL_SET_CLOSE:
212
0
      BIO_set_shutdown(b, static_cast<int>(num));
213
0
      return 1;
214
0
    case BIO_CTRL_FLUSH:
215
0
      return fflush(fp) == 0;
216
0
    default:
217
0
      return 0;
218
0
  }
219
0
}
220
221
0
static int file_gets(BIO *bp, char *buf, int size) {
222
0
  if (size == 0) {
223
0
    return 0;
224
0
  }
225
226
0
  if (!fgets(buf, size, (FILE *)BIO_get_data(bp))) {
227
0
    buf[0] = 0;
228
    // TODO(davidben): This doesn't distinguish error and EOF. This should check
229
    // |ferror| as in |file_read|.
230
0
    return 0;
231
0
  }
232
233
0
  return (int)strlen(buf);
234
0
}
235
236
static const BIO_METHOD methods_filep = {
237
    BIO_TYPE_FILE,      "FILE pointer", file_write,
238
    file_read,          file_gets,      file_ctrl,
239
    /*create=*/nullptr, file_free,      /*callback_ctrl=*/nullptr,
240
};
241
242
0
const BIO_METHOD *BIO_s_file() { return &methods_filep; }
243
244
245
0
int BIO_get_fp(BIO *bio, FILE **out_file) {
246
0
  return (int)BIO_ctrl(bio, BIO_C_GET_FILE_PTR, 0, (char *)out_file);
247
0
}
248
249
0
int BIO_set_fp(BIO *bio, FILE *file, int flags) {
250
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILE_PTR, flags, (char *)file);
251
0
}
252
253
0
int BIO_read_filename(BIO *bio, const char *filename) {
254
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_READ,
255
0
                       (char *)filename);
256
0
}
257
258
0
int BIO_write_filename(BIO *bio, const char *filename) {
259
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_WRITE,
260
0
                       (char *)filename);
261
0
}
262
263
0
int BIO_append_filename(BIO *bio, const char *filename) {
264
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_APPEND,
265
0
                       (char *)filename);
266
0
}
267
268
0
int BIO_rw_filename(BIO *bio, const char *filename) {
269
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME,
270
0
                       BIO_CLOSE | BIO_FP_READ | BIO_FP_WRITE,
271
0
                       (char *)filename);
272
0
}
273
274
0
long BIO_tell(BIO *bio) { return BIO_ctrl(bio, BIO_C_FILE_TELL, 0, nullptr); }
275
276
0
long BIO_seek(BIO *bio, long offset) {
277
0
  return BIO_ctrl(bio, BIO_C_FILE_SEEK, offset, nullptr);
278
0
}